1

I have the following code

conn = MySQLdb.connect(server, user, password, database, autocommit=True, charset='utf8')
    for e in list_to_updpate:
        nid = e[0]
        vid = e[1]
        text = e[2]
        delta = e[3]
        deleted = e[4]
        langcode = e[5]
        to_update = e[6]
        if (to_update == 1):
            with conn.cursor() as cursor:
                cursor.execute("""update node__body set body_value=%s 
                                     where entity_id=%s AND revision_id=%s AND
                                     delta=%s AND deleted=%s AND langcode=%s;""" , 
                               (MySQLdb.escape_string(text) , int(nid), 
                                int(vid), int(delta), int(deleted), langcode, ))
            conn.commit()
    conn.close()

Where I have the list of entries I want to update and text is a LONG TEXT which can contain anything from the UTF8 table.

When I run the code I notice 2 things:

  1. It runs for a while but nothing gets updated.
  2. At some point it crashes with the error:

    _mysql_exceptions.OperationalError: (1242, 'Subquery returns more than 1 row')
    

This I can not get a grip of it and more I am sure that only one row gets updated since as per table definition the primary key is defined as (entity_id, revision_id, delta, deleted, langcode)

Note: This behavior is repeated with pymysql and with MySQL-client on python3.6

Regards, T

Todor Kostov
  • 107
  • 1
  • 1
  • 10
  • Have you tried to manually execute the update query using mysql command line or some other GUI client? – Ravi Chandra Apr 17 '17 at 13:10
  • I can not, since the text variable contains the source code of an entire web page. However I did a select from the table with the same where clause and for each of the queries it returned 1 row. This means the problem is somewhere with the text variable.... however I am not sure what – Todor Kostov Apr 17 '17 at 13:23
  • If the text variable contains single quote or double quote, the query will fail to execute. Since the text is HTML, you need to do encode it before storing in to the database column. – Ravi Chandra Apr 17 '17 at 13:25
  • Yes, That is why I pass MySQLdb.escape_string(text) which should take care of sql special characters. Also if I am correct this should not be needed since using the %s and the query is build automatically. Also note that the queries execute up to some point with no error, but nothing gets committed to the DB. – Todor Kostov Apr 17 '17 at 13:35
  • I'm not sure if this approach has worked for you other times, but it seems odd to me that when you use cursor, is inside a with, which means that the commit will never be reached with the cursor opened, as it'll be closed. – valeas Apr 17 '17 at 13:46

2 Answers2

1

Found the answer:

It turns out that in order to match the LONGTEXT of the mysql db one should use io.String() object to pass the text. Otherwise it does not behave as expected. Mostly you can expect that a simple string would be mapped to varchar which should be at a max length. Be careful, since a varchar can is a subset of LONG TEXT you can expect that no error would be raised if the query commits and the input would be cropped if it gets committed to the DB. Why in this particular case it never committed(see discussion in the question) is still unclear to me. Now the working code:

conn = MySQLdb.connect(server, user, password, database, autocommit=True, charset='utf8')
for e in list_to_update:
    cursor = conn.cursor()
    nid = e[0]
    vid = e[1]
    text = e[2]
    delta = e[3]
    deleted = e[4]
    langcode = e[5]
    to_update = e[6]
    if to_update == 1:
        cursor.execute(
            "update node__body set body_value=%s where entity_id=%s AND revision_id=%s AND delta=%s AND deleted=%s AND langcode=%s",
            (io.StringIO(text), nid, vid, delta, deleted, langcode))
        conn.commit()
    cursor.close()
conn.close()

Note this is Python3. For Python2 use StringIO.String()

Hope that helps,
Todor

Todor Kostov
  • 107
  • 1
  • 1
  • 10
0

There are a few things I would like to point out before I post the code.

First thing, escape_string is not needed as the execute will do it for you, as long as you include the parameters in a tuple as the second element.

I suspect your problem is around the cursor. Seems that is being opened, used and closed before commiting anything.

If there aren't any elements that go against having the cursor opened for as long as needed to update all the elements in the list, I suggest this approach.

conn = MySQLdb.connect(server, user, password, database, autocommit=True, charset='utf8')
cursor = conn.cursor()
for e in list_to_update:
    nid = e[0]
    vid = e[1]
    text = e[2]
    delta = e[3]
    deleted = e[4]
    langcode = e[5]
    to_update = e[6]

    if to_update == 1:
        cursor.execute("update node__body set body_value=%s where entity_id=%s AND revision_id=%s AND delta=%s AND deleted=%s AND langcode=%s" , 
                               (text , nid, vid, delta, deleted, langcode))
        conn.commit()
    cursor.close()
    conn.close()

If you need to make only one commit, right at the end, you only need to edit the commit line removing one indent. Also, note that i've added cursor.close, as I'm not using the with approach.

valeas
  • 364
  • 1
  • 7
  • 18
  • Thank you for the suggestion! However I think the cursor.close() and conn.close() are not in their right places. I just deployed it with 2 different openings of the cursor and the corresponding closings, but it crashed again with _mysql_exceptions.OperationalError: (1242, 'Subquery returns more than 1 row') – Todor Kostov Apr 17 '17 at 14:01
  • I've read on one of your comments that you did try a select and it returned only 1 row for any element in the `list_to_update`. But maybe you could try adding `LIMIT 1` in the query and trying again. – valeas Apr 17 '17 at 14:08
  • I did something else: Removed the row which created the problem. Now the update runs, but after that no changes are visible in the database. – Todor Kostov Apr 17 '17 at 14:32
  • This problem seems much more easier to solve, on paper at least. The `commit`in your code is reachable and is being called, right? I did encounter something similar a while ago, and after a few hours it end up being that the `commit` was not called. – valeas Apr 17 '17 at 14:35
  • Yes and no. It seems to be called. Also there is the autocommit=True in the connection setting which as far as I understood turns commits upon execution of each cursor.... I am new to python DB world but from the reading I did it seems all right but the text. – Todor Kostov Apr 17 '17 at 14:44
  • The specifics of the table and data that you are updating may be the problem, but if so, I'm tied in my end and can't offer much help with the information I have. – valeas Apr 17 '17 at 14:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/141897/discussion-between-valeas-and-edward-teach). – valeas Apr 17 '17 at 14:49