Repository: googleapis/python-bigquery

Issue:
option to set preserve_ascii_control_characters is not available on load config 

#### Environment details

  - OS type and version: windows 10 
  - Python version: 3.10 
  - pip version: pip 22.0.3
  - `google-cloud-bigquery` version: `Name: google-cloud-bigquery
Version: 3.3.3
Summary: Google BigQuery API client library
Home-page: https://github.com/googleapis/python-bigquery
Author: Google LLC
Author-email: googleapis-packages@google.com
License: Apache 2.0
Location: c:\users\fki\appdata\roaming\python\python39\site-packages
Requires: google-api-core, google-cloud-bigquery-storage, google-cloud-core, google-resumable-media, grpcio, packaging, proto-plus, protobuf, pyarrow, python-dateutil, requests
Required-by: bigquery, pandas-gbq`

#### Steps to reproduce

  1. /google/cloud/bigquery/job/load.py LoadJobConfig class does not have a property set preserve_ascii_control_characters options 
 

#### Work around 
 job_config._properties["load"]["preserve_ascii_control_characters"] = True


Discussion:
let me know if you think this needs to be fixed, I can create a pull request for this 
For reference the REST documentation is here: https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#JobConfigurationLoad.FIELDS.preserve_ascii_control_characters

Resolving patch:
diff --git a/google/cloud/bigquery/job/load.py b/google/cloud/bigquery/job/load.py
--- a/google/cloud/bigquery/job/load.py
+++ b/google/cloud/bigquery/job/load.py
@@ -311,6 +311,19 @@ def null_marker(self):
     def null_marker(self, value):
         self._set_sub_prop("nullMarker", value)
 
+    @property
+    def preserve_ascii_control_characters(self):
+        """Optional[bool]: Preserves the embedded ASCII control characters when sourceFormat is set to CSV.
+
+        See:
+        https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#JobConfigurationLoad.FIELDS.preserve_ascii_control_characters
+        """
+        return self._get_sub_prop("preserveAsciiControlCharacters")
+
+    @preserve_ascii_control_characters.setter
+    def preserve_ascii_control_characters(self, value):
+        self._set_sub_prop("preserveAsciiControlCharacters", bool(value))
+
     @property
     def projection_fields(self) -> Optional[List[str]]:
         """Optional[List[str]]: If


Test patch:
diff --git a/tests/unit/job/test_load_config.py b/tests/unit/job/test_load_config.py
--- a/tests/unit/job/test_load_config.py
+++ b/tests/unit/job/test_load_config.py
@@ -424,6 +424,20 @@ def test_null_marker_setter(self):
         config.null_marker = null_marker
         self.assertEqual(config._properties["load"]["nullMarker"], null_marker)
 
+    def test_preserve_ascii_control_characters_missing(self):
+        config = self._get_target_class()()
+        self.assertIsNone(config.preserve_ascii_control_characters)
+
+    def test_preserve_ascii_control_characters_hit(self):
+        config = self._get_target_class()()
+        config._properties["load"]["preserveAsciiControlCharacters"] = True
+        self.assertTrue(config.preserve_ascii_control_characters)
+
+    def test_preserve_ascii_control_characters_setter(self):
+        config = self._get_target_class()()
+        config.preserve_ascii_control_characters = True
+        self.assertTrue(config._properties["load"]["preserveAsciiControlCharacters"])
+
     def test_projection_fields_miss(self):
         config = self._get_target_class()()
         self.assertIsNone(config.projection_fields)