Invalid request URL for Fusion Tables insert

69 views
Skip to first unread message

ferenc....@bitongo.com

unread,
Apr 22, 2013, 7:02:45 PM4/22/13
to google-api-p...@googlegroups.com
Hi,

Trying to use the module for accessing FusionTables (downloaded the most recent google-api-python-client-gae-1.1.zip).

Authentication works fine and tables can be listed however inserting new rows to tables result in InvalidURL exception.

The SQL insert is not sent with POST but with GET and is probably too big.
POST should be used:
..
  "query": {
   "methods": {
    "sql": {
     "id": "fusiontables.query.sql",
     "path": "query",
     "httpMethod": "POST",
..


Is there a way to force the service client to use the POST method?


Ferenc

Joe Gregorio

unread,
Apr 22, 2013, 10:10:58 PM4/22/13
to google-api-p...@googlegroups.com
The client library will automatically switch from GET to POST
if the URL length becomes too long:

https://code.google.com/p/google-api-python-client/source/browse/apiclient/http.py#663
> --
> You received this message because you are subscribed to the Google Groups
> "Google API Python Client" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to google-api-python-...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

Ferenc Vehmann

unread,
Apr 23, 2013, 4:26:16 AM4/23/13
to google-api-p...@googlegroups.com
Hi Joe,

Thanks for your quick response.

This is interesting.
At http.py:633  the URI len is bigger than the max but self.method == 'POST' and so no conversion is made.
So the question is why is the method set to POST when a GET request is constructed.
self.body is empty.

Ferenc


--
Ferenc Vehmann
Co-Founder

Bitongo Ltd.

Joe Gregorio

unread,
Apr 23, 2013, 9:46:40 AM4/23/13
to google-api-p...@googlegroups.com
OK, now I understand, the SQL you supply goes in the sql parameter. If the SQL
were in a body= parameter then it would go in the body of the POST request
and wouldn't affect the URL length. You should definitely bring this up in the
FusionTables API user's group as an issue with the API:

https://developers.google.com/fusiontables/groups/fusionTablesApiGroup

You might be able to work around this limitation by putting your request in
a batch:

https://developers.google.com/api-client-library/python/guide/batch

The should let you get by the URL length limit on GAE. Please let me
know if this works for you.

-joe

Ferenc Vehmann

unread,
Apr 23, 2013, 10:41:52 AM4/23/13
to google-api-p...@googlegroups.com
Using body= results in an error:

service.query().sql(body = sql_query)

  File "..../apiclient/discovery.py", line 573, in method

    raise TypeError('Got an unexpected keyword argument "%s"' % name)

TypeError: Got an unexpected keyword argument "body"

Batching is not really an options, at least not a straight forward one as the data is one line.

However, I've managed to modify http.py so that it does put the date into the body.
http.py:663

      if len(self.uri) > MAX_URI_LENGTH: # and self.method == 'GET':

        self.method = 'POST'

        # self.headers['x-http-method-override'] = 'GET'

However, this is clearly not nice.

I still don't understand why the query parameters end up in GET as there is POST in the discovery.
..
  "query": {
   "methods": {
    "sql": {
     "id": "fusiontables.query.sql",
     "path": "query",
     "httpMethod": "POST",
..

Joe Gregorio

unread,
Apr 23, 2013, 10:46:19 AM4/23/13
to google-api-p...@googlegroups.com
On Tue, Apr 23, 2013 at 10:41 AM, Ferenc Vehmann
<ferenc....@bitongo.com> wrote:
> Using body= results in an error:
>
> service.query().sql(body = sql_query)
>
> File "..../apiclient/discovery.py", line 573, in method
>
> raise TypeError('Got an unexpected keyword argument "%s"' % name)
>
> TypeError: Got an unexpected keyword argument "body"

Sorry, I wasn't suggesting you use body, just explaining that the function
signature would be different if the SQL was sent in the body of the
request.

>
> Batching is not really an options, at least not a straight forward one as
> the data is one line.

Use batching, take the singe request you are making, put it in a batch
request like the code I linked to, and then execute that batch. The request
should work as it did before, but it will not hit the URL limit from App Engine.

>
> However, I've managed to modify http.py so that it does put the date into
> the body.
> http.py:663
>
> if len(self.uri) > MAX_URI_LENGTH: # and self.method == 'GET':
>
> self.method = 'POST'
>
> # self.headers['x-http-method-override'] = 'GET'
>
> However, this is clearly not nice.
>
> I still don't understand why the query parameters end up in GET as there is
> POST in the discovery.
> ..
>
> "query": {
> "methods": {
> "sql": {
> "id": "fusiontables.query.sql",
> "path": "query",
> "httpMethod": "POST",

Because the sql parameter "location" is "query" which means to put it
in the query parameters:

"parameters": {
"hdrs": {
"type": "boolean",
"description": "Should column names be included (in the first
row)?. Default is true.",
"location": "query"
},
"sql": {
"type": "string",
"description": "An SQL
SELECT/SHOW/DESCRIBE/INSERT/UPDATE/DELETE/CREATE statement.",
"required": true,
"location": "query"

Ferenc Vehmann

unread,
Apr 23, 2013, 10:54:48 AM4/23/13
to google-api-p...@googlegroups.com
Thanks for the batching suggestion. I don't want to break lines.
I think this is just as nasty as modifying the http.py code so that it does POST  at the end.

Are you suggesting that the signature should be something like this:
      "sql": {
       "type": "string",
       "description": "An SQL
SELECT/SHOW/DESCRIBE/INSERT/UPDATE/DELETE/CREATE statement.",
       "required": true,
       "location": "body"                       <<<<<<<<<<<<< changed

Joe Gregorio

unread,
Apr 23, 2013, 11:00:55 AM4/23/13
to google-api-p...@googlegroups.com
No. I am suggesting you take your code:


kml = "<LineString><coordinates>lng,lat lng,lat ..</coordinates></LineString>"
sql = 'INSERT INTO %s (track) VALUES (\'%s\')' % (table_id, kml)
insert_response = service.query().sql(sql = sql).execute()


And change it to:

def do_something(request_id, response, exception):
if exception is not None:
# Do something with the exception
pass
else:
# Do something with the response
pass

kml = "<LineString><coordinates>lng,lat lng,lat ..</coordinates></LineString>"
sql = 'INSERT INTO %s (track) VALUES (\'%s\')' % (table_id, kml)
insert_request = service.query().sql(sql = sql)

batch = BatchHttpRequest()
batch.add(insert_request, callback=do_something)
batch.execute()


On Tue, Apr 23, 2013 at 10:54 AM, Ferenc Vehmann

Ferenc Vehmann

unread,
Apr 23, 2013, 11:42:30 AM4/23/13
to google-api-p...@googlegroups.com
This results in: 
HttpError: <HttpError 400 when requesting https://www.googleapis.com/batch returned "Bad Request">

What I didn't understand is what exactly needs to be changed in the Fusion Tables API signature so that the python client will put the SQL parameter in the POST body.

Joe Gregorio

unread,
Apr 23, 2013, 12:05:55 PM4/23/13
to google-api-p...@googlegroups.com
There is nothing you can do to change this, it has to be changed
on the server side.

On Tue, Apr 23, 2013 at 11:42 AM, Ferenc Vehmann

Ferenc Vehmann

unread,
Apr 23, 2013, 12:18:19 PM4/23/13
to google-api-p...@googlegroups.com
OK.
I am sticking with the http.py modification as long as the client the the fusion api are not working nicely together.

Thanks for your help.

Ferenc

Tom R

unread,
Jul 14, 2013, 4:46:45 AM7/14/13
to google-api-p...@googlegroups.com
Has the status of this changed (Google fixed it on the server)? Are so few people using the API client with the FusionTables API?

I'm running into the problem described here and have yet to try either of the workarounds mentioned.
Reply all
Reply to author
Forward
0 new messages