Cassandra
  1. Cassandra
  2. CASSANDRA-5563

The CQL3 binary protocol does not allow a user to bind an empty buffer to signify the start of the token range

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Minor Minor
    • Resolution: Cannot Reproduce
    • Fix Version/s: None
    • Component/s: API
    • Labels:
      None

      Description

      Using CQL2 or CQL3 over thrift, one can issue a query which starts at the beginning of the table by binding an empty buffer. The same is true for CQL2 using calls like get_range_slice. This is not allowed with the binary protocol. Here is working sample code for CQL3 over thrift:

      
          CqlPreparedResult stmt = _client.prepare_cql3_query(ByteBuffer.wrap("SELECT \" Sequence number\" from \"nhanes52simple\" where TOKEN (\" Sequence number\") > TOKEN(?) limit 15".getBytes()), Compression.NONE);
      
          // Bind empty buffer to get query to start at the beginning of 
          // the table
          ByteBuffer b = ByteBuffer.wrap(new byte[0]);
          bindVars.add(b);
      
          int cnt = 0;
          CqlResult result;
      
          do {
            result =  _client.execute_prepared_cql3_query(stmt.itemId, bindVars, ConsistencyLevel.ONE);
            // Set up the next chunk, by setting the bind var to the last received key
            bindVars.set(0, ByteBuffer.wrap(result.getRows()
                    .get(result.getRows().size() - 1).getColumns().get(0).getValue()));
      
            // Count rows
            cnt += result.getRows().size();
      
            if (cnt > 100)
              Assert.fail("Running past the end of the table: cnt = " + cnt + ", size() =  " + result.getRows().size());
      
          } while (result.getRows().size() >= CHUNK_SIZE);
      
          Assert.assertEquals("Wrong count", 100, cnt);
      
        } 
      

        Activity

        Hide
        Jonathan Ellis added a comment -

        You didn't include your statement. Is it using token()?

        Show
        Jonathan Ellis added a comment - You didn't include your statement. Is it using token() ?
        Hide
        Steven Lowenthal added a comment -

        Code amended. It does use token()

        Show
        Steven Lowenthal added a comment - Code amended. It does use token()
        Hide
        Aleksey Yeschenko added a comment -

        Steven is suggesting making token() function accept null (and native proto supports binding null) to make paging through rows possible in CQL3 using one single prepared query.

        Show
        Aleksey Yeschenko added a comment - Steven is suggesting making token() function accept null (and native proto supports binding null) to make paging through rows possible in CQL3 using one single prepared query.
        Hide
        Sylvain Lebresne added a comment -

        I'm not able to reproduce this. I've just tested it and binding an empty buffer to a bind marker that is the argument to token() works fine. And as far as I can tell, the native protocol has always allowed to bind empty byte buffers so I'm not sure that has ever be a problem, but if it was, it's fixed now as far as I can tell.

        I'll note that the fact that the token of an empty buffer happens to return the minimum token is a bit of an implementation detail of our IPartitioner.getToken() methods. And in particular, with composite partition key you won't really be able to provide a empty byte buffer for the whole partition key, so you will have to use more than a single prepared statement to page in that case.

        As for allowing null as argument to token(), I'm not a fan because:

        1. I don't find token(null) particularly logical/clean.
        2. This doesn't really transpose well to composite partition key (for which the token() method really expect multiple arguments).
        3. With the native protocol 2.0 supporting a much simpler way to do paging, I don't see a point in bothering here.
        Show
        Sylvain Lebresne added a comment - I'm not able to reproduce this. I've just tested it and binding an empty buffer to a bind marker that is the argument to token() works fine. And as far as I can tell, the native protocol has always allowed to bind empty byte buffers so I'm not sure that has ever be a problem, but if it was, it's fixed now as far as I can tell. I'll note that the fact that the token of an empty buffer happens to return the minimum token is a bit of an implementation detail of our IPartitioner.getToken() methods. And in particular, with composite partition key you won't really be able to provide a empty byte buffer for the whole partition key, so you will have to use more than a single prepared statement to page in that case. As for allowing null as argument to token(), I'm not a fan because: I don't find token(null) particularly logical/clean. This doesn't really transpose well to composite partition key (for which the token() method really expect multiple arguments). With the native protocol 2.0 supporting a much simpler way to do paging, I don't see a point in bothering here.

          People

          • Assignee:
            Sylvain Lebresne
            Reporter:
            Steven Lowenthal
          • Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development