Repository: googleapis/python-bigquery

Issue:
support load job option ColumnNameCharacterMap
Support new field ColumnNameCharacterMap: https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#columnnamecharactermap

Discussion:

Patch:
diff --git a/google/cloud/bigquery/job/load.py b/google/cloud/bigquery/job/load.py
index 17643545..e56ce16f 100644
--- a/google/cloud/bigquery/job/load.py
+++ b/google/cloud/bigquery/job/load.py
@@ -32,6 +32,26 @@ from google.cloud.bigquery.job.base import _JobReference
 from google.cloud.bigquery.query import ConnectionProperty
 
 
+class ColumnNameCharacterMap:
+    """Indicates the character map used for column names.
+
+    https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#columnnamecharactermap
+    """
+
+    COLUMN_NAME_CHARACTER_MAP_UNSPECIFIED = "COLUMN_NAME_CHARACTER_MAP_UNSPECIFIED"
+    """Unspecified column name character map."""
+
+    STRICT = "STRICT"
+    """Support flexible column name and reject invalid column names."""
+
+    V1 = "V1"
+    """	Support alphanumeric + underscore characters and names must start with
+    a letter or underscore. Invalid column names will be normalized."""
+
+    V2 = "V2"
+    """Support flexible column name. Invalid column names will be normalized."""
+
+
 class LoadJobConfig(_JobConfig):
     """Configuration options for load jobs.
 
@@ -597,6 +617,27 @@ class LoadJobConfig(_JobConfig):
         else:
             self._del_sub_prop("parquetOptions")
 
+    @property
+    def column_name_character_map(self) -> str:
+        """Optional[google.cloud.bigquery.job.ColumnNameCharacterMap]:
+        Character map supported for column names in CSV/Parquet loads. Defaults
+        to STRICT and can be overridden by Project Config Service. Using this
+        option with unsupported load formats will result in an error.
+
+        See
+        https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#JobConfigurationLoad.FIELDS.column_name_character_map
+        """
+        return self._get_sub_prop(
+            "columnNameCharacterMap",
+            ColumnNameCharacterMap.COLUMN_NAME_CHARACTER_MAP_UNSPECIFIED,
+        )
+
+    @column_name_character_map.setter
+    def column_name_character_map(self, value: Optional[str]):
+        if value is None:
+            value = ColumnNameCharacterMap.COLUMN_NAME_CHARACTER_MAP_UNSPECIFIED
+        self._set_sub_prop("columnNameCharacterMap", value)
+
 
 class LoadJob(_AsyncJob):
     """Asynchronous job for loading data into a table.


Test patch:
diff --git a/tests/unit/job/test_load_config.py b/tests/unit/job/test_load_config.py
index e1fa2641..becf3e95 100644
--- a/tests/unit/job/test_load_config.py
+++ b/tests/unit/job/test_load_config.py
@@ -843,3 +843,42 @@ class TestLoadJobConfig(_Base):
 
         config.parquet_options = None
         self.assertNotIn("parquetOptions", config._properties["load"])
+
+    def test_column_name_character_map_missing(self):
+        from google.cloud.bigquery.job.load import ColumnNameCharacterMap
+
+        config = self._get_target_class()()
+        self.assertEqual(
+            config.column_name_character_map,
+            ColumnNameCharacterMap.COLUMN_NAME_CHARACTER_MAP_UNSPECIFIED,
+        )
+
+    def test_column_name_character_map_hit(self):
+        from google.cloud.bigquery.job.load import ColumnNameCharacterMap
+
+        config = self._get_target_class()()
+        config._properties["load"]["columnNameCharacterMap"] = "STRICT"
+        self.assertEqual(
+            config.column_name_character_map,
+            ColumnNameCharacterMap.STRICT,
+        )
+
+    def test_column_name_character_map_setter(self):
+        from google.cloud.bigquery.job.load import ColumnNameCharacterMap
+
+        config = self._get_target_class()()
+        config.column_name_character_map = "V1"
+        self.assertEqual(
+            config._properties["load"]["columnNameCharacterMap"],
+            ColumnNameCharacterMap.V1,
+        )
+
+    def test_column_name_character_map_none(self):
+        from google.cloud.bigquery.job.load import ColumnNameCharacterMap
+
+        config = self._get_target_class()()
+        config.column_name_character_map = None
+        self.assertEqual(
+            config._properties["load"]["columnNameCharacterMap"],
+            ColumnNameCharacterMap.COLUMN_NAME_CHARACTER_MAP_UNSPECIFIED,
+        )