Merge lp:~hingo/drizzle/drizzle-execute-result_set-off-by-one into lp:~drizzle-trunk/drizzle/development

Proposed by Henrik Ingo
Status: Merged
Approved by: Mark Atwood
Approved revision: 2458
Merged at revision: 2480
Proposed branch: lp:~hingo/drizzle/drizzle-execute-result_set-off-by-one
Merge into: lp:~drizzle-trunk/drizzle/development
Diff against target: 85 lines (+16/-3)
1 file modified
drizzled/plugin/client/cached.h (+16/-3)
To merge this branch: bzr merge lp:~hingo/drizzle/drizzle-execute-result_set-off-by-one
Reviewer Review Type Date Requested Status
Drizzle Merge Team Pending
Review via email: mp+86791@code.launchpad.net

Description of the change

Execute API has off by one error, there is always an empty row at the
end of each result set. This fixes it.

Sorry I have no test for this, I don't know who or what code uses this
in the server except for json_server, which has no tests currently.

Ref: http://www.flamingspork.com/blog/2011/04/21/http-json-alsosql-interface-to-drizzle/comment-page-1/#comment-102142

To post a comment you must log in.
Revision history for this message
David Shrewsbury (dshrews) wrote :

Hi Henrik,

The slave plugin makes extensive use of the Execute API. We should verify that the replication tests still pass with these changes. Running it through param-build should check that.

-Dave

Revision history for this message
Henrik Ingo (hingo) wrote :

Thanks Shrews for enlightening me!

Of course, it would be even better to have a test somewhere that fails without this patch. But I'm glad to hear this area is covered by some tests, at least we should find out soon if I broke something or not.

Revision history for this message
Henrik Ingo (hingo) wrote :

Just to document it for our children and whatnot, Shrews pointed me to the code in slave plugin that is affected by this:
queue_consumer.cc:198

****************************************************************

  uint32_t found_rows= 0;
  while (result_set.next())
  {
    string msg= result_set.getString(0);
    string com_id= result_set.getString(1);
    string orig_server_uuid= result_set.getString(2);
    string orig_commit_id= result_set.getString(3);

    if ((msg == "") || (found_rows == 1))
      break;

    /* No columns should be NULL */
    assert(result_set.isNull(0) == false);
    assert(result_set.isNull(1) == false);
    assert(result_set.isNull(2) == false);
    assert(result_set.isNull(3) == false);

    google::protobuf::TextFormat::ParseFromString(msg, &transaction);

    commit_id= com_id;
    originating_server_uuid= orig_server_uuid;
    originating_commit_id= boost::lexical_cast<uint64_t>(orig_commit_id);
    found_rows++;
  }

  if (found_rows == 0)
    return false;

  return true;
****************************************************************

The code will continue to work properly because of:

  while (result_set.next())

The code currently works because it checks to omit the empty row at:
    if ((msg == "") || (found_rows == 1))
      break;

(It seems to me this is there specifically to work around this bug and could now be removed.)

Revision history for this message
Mark Atwood (fallenpegasus) wrote :

Turns out it does fail in a test

http://jenkins.drizzle.org/view/Drizzle-build/job/drizzle-build-ubuntu10.10-32bit/1151/console

json_server.basic [ fail ]
--- ../plugin/json_server/tests/r/basic.result 2011-12-28 05:06:28.000000000 +0300
+++ /home/hudson/hudson/workspace/drizzle-build-ubuntu10.10-32bit/drizzle7-2011.11.29.2480/_build/tests/var/log/basic.reject 2011-12-28 05:29:35.000000000 +0300
@@ -3,9 +3,6 @@
 http_post("http://localhost:PORT/0.1/sql", 'select * from t1;')
 {
    "query" : "select * from t1;",
- "result_set" : [
- [ "", "" ]
- ],
    "sqlstate" : "00000"
 }

@@ -15,8 +12,7 @@
 {
    "query" : "select * from t1;",
    "result_set" : [
- [ "1", "from MySQL protocol" ],
- [ "", "" ]
+ [ "1", "from MySQL protocol" ]
    ],
    "sqlstate" : "00000"
 }

drizzletest: Result content mismatch

Revision history for this message
Henrik Ingo (hingo) wrote :

More than anything else I'm surprised and confused to realize json_server has a tests directory and I've not seen it until now :-) Apologies for proposing to merge before running make test myself, I don't usually do that, in this case my brain apparently had just really decided that there is nothing to test, which is never true...

Seems you realized already that this test is wrong and the empty line needs to be removed from result set.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'drizzled/plugin/client/cached.h'
--- drizzled/plugin/client/cached.h 2011-07-25 04:35:53 +0000
+++ drizzled/plugin/client/cached.h 2011-12-23 10:04:12 +0000
@@ -58,7 +58,8 @@
58 max_column++;58 max_column++;
59 }59 }
60 _result_set->setColumnCount(max_column);60 _result_set->setColumnCount(max_column);
61 _result_set->createRow();61 // Moved to checkRowBegin()
62 //_result_set->createRow();
62 }63 }
6364
64 virtual void sendError(drizzled::error_t error_code, const char *error_message)65 virtual void sendError(drizzled::error_t error_code, const char *error_message)
@@ -66,14 +67,19 @@
66 _result_set->pushException(sql::Exception(error_message, error_code));67 _result_set->pushException(sql::Exception(error_message, error_code));
67 }68 }
6869
69 virtual void checkRowEnd()70 virtual void checkRowBegin()
70 {71 {
71 if (++column % max_column == 0)72 if (currentColumn() == 0)
72 {73 {
73 _result_set->createRow();74 _result_set->createRow();
74 }75 }
75 }76 }
7677
78virtual void checkRowEnd()
79 {
80 column++;
81 }
82
77 using Client::store;83 using Client::store;
7884
79 virtual void store(Field *from)85 virtual void store(Field *from)
@@ -90,42 +96,49 @@
9096
91 virtual void store()97 virtual void store()
92 {98 {
99 checkRowBegin();
93 _result_set->setColumnNull(currentColumn());100 _result_set->setColumnNull(currentColumn());
94 checkRowEnd();101 checkRowEnd();
95 }102 }
96103
97 virtual void store(int32_t from)104 virtual void store(int32_t from)
98 {105 {
106 checkRowBegin();
99 _result_set->setColumn(currentColumn(), boost::lexical_cast<std::string>(from));107 _result_set->setColumn(currentColumn(), boost::lexical_cast<std::string>(from));
100 checkRowEnd();108 checkRowEnd();
101 }109 }
102110
103 virtual void store(uint32_t from)111 virtual void store(uint32_t from)
104 {112 {
113 checkRowBegin();
105 _result_set->setColumn(currentColumn(), boost::lexical_cast<std::string>(from));114 _result_set->setColumn(currentColumn(), boost::lexical_cast<std::string>(from));
106 checkRowEnd();115 checkRowEnd();
107 }116 }
108117
109 virtual void store(int64_t from)118 virtual void store(int64_t from)
110 {119 {
120 checkRowBegin();
111 _result_set->setColumn(currentColumn(), boost::lexical_cast<std::string>(from));121 _result_set->setColumn(currentColumn(), boost::lexical_cast<std::string>(from));
112 checkRowEnd();122 checkRowEnd();
113 }123 }
114124
115 virtual void store(uint64_t from)125 virtual void store(uint64_t from)
116 {126 {
127 checkRowBegin();
117 _result_set->setColumn(currentColumn(), boost::lexical_cast<std::string>(from));128 _result_set->setColumn(currentColumn(), boost::lexical_cast<std::string>(from));
118 checkRowEnd();129 checkRowEnd();
119 }130 }
120131
121 virtual void store(double from, uint32_t decimals, String *buffer)132 virtual void store(double from, uint32_t decimals, String *buffer)
122 {133 {
134 checkRowBegin();
123 buffer->set_real(from, decimals, &my_charset_bin);135 buffer->set_real(from, decimals, &my_charset_bin);
124 return store(buffer->ptr(), buffer->length());136 return store(buffer->ptr(), buffer->length());
125 }137 }
126138
127 virtual void store(const char *from, size_t length)139 virtual void store(const char *from, size_t length)
128 {140 {
141 checkRowBegin();
129 _result_set->setColumn(currentColumn(), std::string(from, length));142 _result_set->setColumn(currentColumn(), std::string(from, length));
130 checkRowEnd();143 checkRowEnd();
131 }144 }