Repository: googleapis/python-spanner-django

Issue:
djangotests: ERROR: test_extra_method_select_argument_with_dashes (basic.tests.ModelTest)
```shell
======================================================================
ERROR: test_extra_method_select_argument_with_dashes (basic.tests.ModelTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/google/api_core/grpc_helpers.py", line 79, in next
    return six.next(self._wrapped)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/grpc/_channel.py", line 364, in __next__
    return self._next()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/grpc/_channel.py", line 358, in _next
    raise self
grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with:
	status = StatusCode.INVALID_ARGUMENT
	details = "Syntax error: Unexpected \"-\" [at 1:21]\nSELECT (1) AS dashed-value, (2) AS undashedvalue, basic_article.id, basic_art...\n                    ^"
	debug_error_string = "{"created":"@1575261818.820579000","description":"Error received from peer ipv4:172.217.11.170:443","file":"src/core/lib/surface/call.cc","file_line":1046,"grpc_message":"Syntax error: Unexpected \"-\" [at 1:21]\nSELECT (1) AS dashed-value, (2) AS undashedvalue, basic_article.id, basic_art...\n                    ^","grpc_status":3}"
>

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/emmanuelodeke/Desktop/spanner-orm/django_tests/django/tests/basic/tests.py", line 265, in test_extra_method_select_argument_with_dashes
    self.assertEqual(articles[0].undashedvalue, 2)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/Django-2.2.8.dev20191126193909-py3.7.egg/django/db/models/query.py", line 308, in __getitem__
    qs._fetch_all()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/Django-2.2.8.dev20191126193909-py3.7.egg/django/db/models/query.py", line 1242, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/Django-2.2.8.dev20191126193909-py3.7.egg/django/db/models/query.py", line 55, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/Django-2.2.8.dev20191126193909-py3.7.egg/django/db/models/sql/compiler.py", line 1133, in execute_sql
    return list(result)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/Django-2.2.8.dev20191126193909-py3.7.egg/django/db/models/sql/compiler.py", line 1512, in cursor_iter
    for rows in iter((lambda: cursor.fetchmany(itersize)), sentinel):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/Django-2.2.8.dev20191126193909-py3.7.egg/django/db/models/sql/compiler.py", line 1512, in <lambda>
    for rows in iter((lambda: cursor.fetchmany(itersize)), sentinel):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/Django-2.2.8.dev20191126193909-py3.7.egg/django/db/utils.py", line 96, in inner
    return func(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/spanner/dbapi/cursor.py", line 218, in fetchmany
    items.append(tuple(self.__next__()))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/spanner/dbapi/cursor.py", line 186, in __next__
    return next(self.__itr)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/google/cloud/spanner_v1/streamed.py", line 143, in __iter__
    self._consume_next()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/google/cloud/spanner_v1/streamed.py", line 116, in _consume_next
    response = six.next(self._response_iterator)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/google/cloud/spanner_v1/snapshot.py", line 45, in _restart_on_unavailable
    for item in iterator:
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/google/api_core/grpc_helpers.py", line 81, in next
    six.raise_from(exceptions.from_grpc_error(exc), exc)
  File "<string>", line 3, in raise_from
google.api_core.exceptions.InvalidArgument: 400 Syntax error: Unexpected \"-\" [at 1:21]\nSELECT (1) AS dashed-value, (2) AS undashedvalue, basic_article.id, basic_art...\n                    ^
```

But really Cloud Spanner doesn't support dashed values as per
```SQL
SELECT (1) AS dashed-value, blogpost.post_id FROM blogpost
```


Discussion:
I'd think dashed values would be supported as long as they're quoted. When I changed `DatabaseOperations.quote_name()` to add backticks around values, however, I ran into other exceptions.
>I'd think dashed values would be supported as long as they're quoted. When I changed DatabaseOperations.quote_name() to add backticks around values, however, I ran into other exceptions

So after that quoting, what would
>SELECT (1) AS dashed-value, blogpost.post_id FROM blogpost

look like?
My understanding is that quoting with backticks looks like this:
```
`dashed-value`
```
Got it, yes that works, thanks!

>SELECT (1) AS `dashed-value`, id FROM django_migrations

<img width="978" alt="Screen Shot 2019-12-02 at 11 02 02 AM" src="https://user-images.githubusercontent.com/4898263/69986915-33fc4280-14f3-11ea-966e-d12a5e962830.png">
and I invited you some days ago to that project so you'll be able to access the UI.

Resolving patch:
diff --git a/spanner/django/operations.py b/spanner/django/operations.py
--- a/spanner/django/operations.py
+++ b/spanner/django/operations.py
@@ -17,6 +17,8 @@ class DatabaseOperations(BaseDatabaseOperations):
     }
 
     def quote_name(self, name):
+        if '-' in name:
+            return '`' + name + '`'
         return name
 
     def bulk_insert_sql(self, fields, placeholder_rows):


Test patch: