Repository: great-expectations/great_expectations

Issue:
Generate expectations fails STRING type in BigQuery
**Describe the bug**
When trying to generate expectations from column STRING by scanning historic data, to many different values where found in the sample and raise a profiling error `Too many query parameters: 30856 exceeds limit of 10000.`

**To Reproduce**
Steps to reproduce the behavior:
1. `great_expectations --v3-api suite new`
2. `How would you like to create your Expectation Suite` -> `3. Automatically, using a profiler`
3. `Select a datasource` -> `datasource_BigQuery_analytics`
```datasources:
  datasource_BigQuery_analytics:
    connection_string: bigquery://my-project/my_dataset
    introspection:
      whole_table:
        data_asset_name_suffix: __whole_table
    module_name: great_expectations.datasource
    class_name: SimpleSqlalchemyDatasource
```
4. Provide: name of the table, temporary table, name of the file

5. Running JupyterNotebook cells:
```
profiler = UserConfigurableProfiler(
    profile_dataset=validator,
    excluded_expectations=None,
    ignored_columns=ignored_columns,
    not_null_only=False,
    primary_or_compound_key=False,
    semantic_types_dict=None,
    table_expectations_only=False,
    value_set_threshold="MANY",
)
suite = profiler.build_suite()
```
Errors:
```
BadRequest: 400 POST https://bigquery.googleapis.com/bigquery/v2/projects/jobs?prettyPrint=false: Too many query parameters: 30856 exceeds limit of 10000.

1:SELECT `vehicle_license_plate` AS `unexpected_values`
2:FROM `my-project.my_datset.pb_ge1`
3:WHERE `vehicle_license_plate` IS NOT NULL AND (`vehicle_license_plate` NOT IN (@`vehicle_license_plate_1`, @`vehicle_license_plate_2`, @`vehicle_license_plate_3`, @`vehicle_license_plate_4`, @`vehicle_license_plate_5`, @`vehicle_license_plate_6`, @`vehicle_license_plate_7`,
.....
....
```

**Expected behavior**
Count number of distinct value found for column. If > 10000, do not run the `unexpected_values` profiling check.

**Environment (please complete the following information):**
 - Operating System: MacOS
 - Great Expectations Version: 0.13.46

**Additional context**
Joining full trace: 
```
Profiling: 0%
0/1 [01:07<?, ?it/s, Column=vehicle_license_plate]

---------------------------------------------------------------------------
BadRequest                                Traceback (most recent call last)
~/Documents/.venv/lib/python3.9/site-packages/great_expectations/execution_engine/execution_engine.py in resolve_metrics(self, metrics_to_resolve, metrics, runtime_configuration)
    375                 try:
--> 376                     resolved_metrics[metric_to_resolve.id] = metric_fn(
    377                         **metric_provider_kwargs

~/Documents/.venv/lib/python3.9/site-packages/great_expectations/expectations/metrics/map_metric_provider.py in _sqlalchemy_column_map_condition_values(cls, execution_engine, metric_domain_kwargs, metric_value_kwargs, metrics, **kwargs)
   2048         val.unexpected_values
-> 2049         for val in execution_engine.engine.execute(query).fetchall()
   2050     ]

~/Documents/.venv/lib/python3.9/site-packages/sqlalchemy/engine/base.py in execute(self, statement, *multiparams, **params)
   2234         connection = self._contextual_connect(close_with_result=True)
-> 2235         return connection.execute(statement, *multiparams, **params)
   2236

~/Documents/.venv/lib/python3.9/site-packages/sqlalchemy/engine/base.py in execute(self, object_, *multiparams, **params)
   1010         else:
-> 1011             return meth(self, multiparams, params)
   1012

~/Documents/.venv/lib/python3.9/site-packages/sqlalchemy/sql/elements.py in _execute_on_connection(self, connection, multiparams, params)
    297         if self.supports_execution:
--> 298             return connection._execute_clauseelement(self, multiparams, params)
    299         else:

~/Documents/.venv/lib/python3.9/site-packages/sqlalchemy/engine/base.py in _execute_clauseelement(self, elem, multiparams, params)
   1123
-> 1124         ret = self._execute_context(
   1125             dialect,

~/Documents/.venv/lib/python3.9/site-packages/sqlalchemy/engine/base.py in _execute_context(self, dialect, constructor, statement, parameters, *args)
   1315         except BaseException as e:
-> 1316             self._handle_dbapi_exception(
   1317                 e, statement, parameters, cursor, context

~/Documents/.venv/lib/python3.9/site-packages/sqlalchemy/engine/base.py in _handle_dbapi_exception(self, e, statement, parameters, cursor, context)
   1513             else:
-> 1514                 util.raise_(exc_info[1], with_traceback=exc_info[2])
   1515

~/Documents/.venv/lib/python3.9/site-packages/sqlalchemy/util/compat.py in raise_(***failed resolving arguments***)
    181         try:
--> 182             raise exception
    183         finally:

~/Documents/.venv/lib/python3.9/site-packages/sqlalchemy/engine/base.py in _execute_context(self, dialect, constructor, statement, parameters, *args)
   1275                 if not evt_handled:
-> 1276                     self.dialect.do_execute(
   1277                         cursor, statement, parameters, context

~/Documents/.venv/lib/python3.9/site-packages/sqlalchemy/engine/default.py in do_execute(self, cursor, statement, parameters, context)
    607     def do_execute(self, cursor, statement, parameters, context=None):
--> 608         cursor.execute(statement, parameters)
    609

~/Documents/.venv/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/_helpers.py in with_closed_check(self, *args, **kwargs)
    494                 raise exc_class(exc_msg)
--> 495             return method(self, *args, **kwargs)
    496

~/Documents/.venv/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/cursor.py in execute(self, operation, parameters, job_id, job_config)
    165         formatted_operation, parameter_types = _format_operation(operation, parameters)
--> 166         self._execute(
    167             formatted_operation, parameters, job_id, job_config, parameter_types

~/Documents/.venv/lib/python3.9/site-packages/google/cloud/bigquery/dbapi/cursor.py in _execute(self, formatted_operation, parameters, job_id, job_config, parameter_types)
    191         config.query_parameters = query_parameters
--> 192         self._query_job = client.query(
    193             formatted_operation, job_config=config, job_id=job_id

~/Documents/.venv/lib/python3.9/site-packages/google/cloud/bigquery/client.py in query(self, query, job_config, job_id, job_id_prefix, location, project, retry, timeout, job_retry)
   3390
-> 3391         future = do_query()
   3392         # The future might be in a failed state now, but if it's

~/Documents/.venv/lib/python3.9/site-packages/google/cloud/bigquery/client.py in do_query()
   3367             try:
-> 3368                 query_job._begin(retry=retry, timeout=timeout)
   3369             except core_exceptions.Conflict as create_exc:

~/Documents/.venv/lib/python3.9/site-packages/google/cloud/bigquery/job/query.py in _begin(self, client, retry, timeout)
   1248         try:
-> 1249             super(QueryJob, self)._begin(client=client, retry=retry, timeout=timeout)
   1250         except exceptions.GoogleAPICallError as exc:

~/Documents/.venv/lib/python3.9/site-packages/google/cloud/bigquery/job/base.py in _begin(self, client, retry, timeout)
    508         span_attributes = {"path": path}
--> 509         api_response = client._call_api(
    510             retry,

~/Documents/.venv/lib/python3.9/site-packages/google/cloud/bigquery/client.py in _call_api(self, retry, span_name, span_attributes, job_ref, headers, **kwargs)
    781             ):
--> 782                 return call()
    783

~/Documents/.venv/lib/python3.9/site-packages/google/api_core/retry.py in retry_wrapped_func(*args, **kwargs)
    285             )
--> 286             return retry_target(
    287                 target,

~/Documents//.venv/lib/python3.9/site-packages/google/api_core/retry.py in retry_target(target, predicate, sleep_generator, deadline, on_error)
    188         try:
--> 189             return target()
    190

~/Documents/.venv/lib/python3.9/site-packages/google/cloud/_http.py in api_request(self, method, path, query_params, data, content_type, headers, api_base_url, api_version, expect_json, _target_object, timeout)
    483         if not 200 <= response.status_code < 300:
--> 484             raise exceptions.from_http_response(response)
    485

BadRequest: 400 POST https://bigquery.googleapis.com/bigquery/v2/projects/jobs?prettyPrint=false: Too many query parameters: 30856 exceeds limit of 10000.

(job ID: f3b804ab-3efa-4bbb-b67f-04cae4571da6)



1:SELECT `vehicle_license_plate` AS `unexpected_values`
2:FROM `my-project.my_datset.pb_ge1`
3:WHERE `vehicle_license_plate` IS NOT NULL AND (`vehicle_license_plate` NOT IN (@`vehicle_license_plate_1`, @`vehicle_license_plate_2`, @`vehicle_license_plate_3`, @`vehicle_license_plate_4`, @`vehicle_license_plate_5`, @`vehicle_license_plate_6`, @`vehicle_license_plate_7`,
.....
....

....
```




Discussion:
Thanks for opening this issue, @80auline! Someone on the team will review and be in touch.

Resolving patch:
diff --git a/great_expectations/expectations/metrics/map_metric_provider.py b/great_expectations/expectations/metrics/map_metric_provider.py
--- a/great_expectations/expectations/metrics/map_metric_provider.py
+++ b/great_expectations/expectations/metrics/map_metric_provider.py
@@ -2043,6 +2043,15 @@ def _sqlalchemy_column_map_condition_values(
     result_format = metric_value_kwargs["result_format"]
     if result_format["result_format"] != "COMPLETE":
         query = query.limit(result_format["partial_unexpected_count"])
+    elif (
+        result_format["result_format"] == "COMPLETE"
+        and "bigquery" in execution_engine.dialect.name
+    ):
+        logger.warning(
+            "BigQuery imposes a limit of 10000 parameters on individual queries; "
+            "if your data contains more than 10000 columns your results will be truncated."
+        )
+        query = query.limit(10000)  # BigQuery upper bound on query parameters
 
     return [
         val.unexpected_values


Test patch: