8000 Correctly handle the length case for 253 bytes - fixes #100 · mauricio/postgresql-async@6148f68 · GitHub
[go: up one dir, main page]

Skip to content
8000
This repository was archived by the owner on Dec 3, 2019. It is now read-only.

Commit 6148f68

Browse files
committed
Correctly handle the length case for 253 bytes - fixes #100
1 parent fbcd302 commit 6148f68

File tree

4 files changed

+55
-32
lines changed

4 files changed

+55
-32
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222

2323
# Changelog
2424

25+
## 0.2.15 - still in progress
26+
27+
* Fixes issue where PostgreSQL decoders fail to produce a NULL value if the null is wrapped by a `Some` instance - #99;
28+
* Fixes issue where the 253 case of length encoded fields on MySQL produce a wrong value;
29+
2530
## 0.2.14 - 2014-08-30
2631

2732
* Remove failed prepared statement from cache - @dboissin - #95

db-async-common/src/main/scala/com/github/mauricio/async/db/util/ChannelWrapper.scala

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,6 @@ class ChannelWrapper( val buffer : ByteBuf ) extends AnyVal {
4343

4444
def readUntilEOF( charset: Charset ) = ByteBufferUtils.readUntilEOF(buffer, charset)
4545

46-
def read3BytesInt : Int = {
47-
val first = buffer.readByte()
48-
val second = buffer.readByte()
49-
val third = buffer.readByte()
50-
var i = third << 16 | second << 8 | first
51-
52-
if ((third & 0x80) == 0x80) {
53-
i |= 0xff000000
54-
}
55-
56-
i
57-
}
58-
5946
def readLengthEncodedString( charset : Charset ) : String = {
6047
val length = readBinaryLength
6148
readFixedString(length.asInstanceOf[Int], charset)
@@ -70,14 +57,22 @@ class ChannelWrapper( val buffer : ByteBuf ) extends AnyVal {
7057
firstByte match {
7158
case MySQL_NULL => -1
7259
case 252 => buffer.readUnsignedShort()
73-
case 253 => read3BytesInt
60+
case 253 => readLongInt
7461
case 254 => buffer.readLong()
7562
case _ => throw new UnknownLengthException(firstByte)
7663
}
7764
}
7865

7966
}
8067

68+
def readLongInt : Int = {
69+
val first = buffer.readByte()
70+
val second = buffer.readByte()
71+
val third = buffer.readByte()
72+
73+
( first & 0xff ) | (( second & 0xff ) << 8) | ((third & 0xff) << 16)
74+
}
75+
8176
def writeLength( length : Long ) {
8277
if (length < 251) {
8378
buffer.writeByte( length.asInstanceOf[Byte])

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/decoder/ResultSetRowDecoder.scala

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,32 @@
1616

1717
package com.github.mauricio.async.db.mysql.decoder
1818

19-
import io.netty.buffer.ByteBuf
19+
import java.nio.charset.Charset
20+
2021
import com.github.mauricio.async.db.mysql.message.server.{ResultSetRowMessage, ServerMessage}
2122
import com.github.mauricio.async.db.util.ChannelWrapper.bufferToWrapper
22-
import java.nio.charset.Charset
23-
import java.nio.ByteOrder
23+
import io.netty.buffer.ByteBuf
2424

2525
object ResultSetRowDecoder {
2626

2727
final val NULL = 0xfb
2828

2929
}
3030

31-
class ResultSetRowDecoder( charset : Charset ) extends MessageDecoder {
31+
class ResultSetRowDecoder(charset: Charset) extends MessageDecoder {
3232

33-
import ResultSetRowDecoder.NULL
33+
import com.github.mauricio.async.db.mysql.decoder.ResultSetRowDecoder.NULL
3434

3535
def decode(buffer: ByteBuf): ServerMessage = {
3636
val row = new ResultSetRowMessage()
3737

38-
while (buffer.isReadable() ) {
39-
if ( buffer.getUnsignedByte(buffer.readerIndex()) == NULL ) {
38+
while (buffer.isReadable()) {
39+
if (buffer.getUnsignedByte(buffer.readerIndex()) == NULL) {
4040
buffer.readByte()
4141
row += null
4242
} else {
4343
val length = buffer.readBinaryLength.asInstanceOf[Int]
4444
row += buffer.readBytes(length)
45-
4645
}
4746
}
4847

mysql-async/src/test/scala/com/github/mauricio/async/db/mysql/QuerySpec.scala

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class QuerySpec extends Specification with ConnectionHelper {
9393
timestamp.getSecondOfMinute === 7
9494

9595

96-
result("created_at_time") === Duration( 3, TimeUnit.HOURS ) + Duration( 14, TimeUnit.MINUTES ) + Duration( 7, TimeUnit.SECONDS )
96+
result("created_at_time") === Duration(3, TimeUnit.HOURS) + Duration(14, TimeUnit.MINUTES) + Duration(7, TimeUnit.SECONDS)
9797

9898
val year = result("created_at_year").asInstanceOf[Short]
9999

@@ -150,21 +150,21 @@ class QuerySpec extends Specification with ConnectionHelper {
150150
| primary key (id) )""".stripMargin
151151

152152
val createIdeas = """CREATE TEMPORARY TABLE ideas (
153-
| id INT NOT NULL AUTO_INCREMENT,
154-
| some_idea VARCHAR(255) NOT NULL,
155-
| primary key (id) )""".stripMargin
153+
| id INT NOT NULL AUTO_INCREMENT,
154+
| some_idea VARCHAR(255) NOT NULL,
155+
| primary key (id) )""".stripMargin
156156

157157
val select = "SELECT * FROM posts"
158158
val selectIdeas = "SELECT * FROM ideas"
159159

160-
val matcher : QueryResult => List[MatchResult[IndexedSeq[String]]] = { result =>
160+
val matcher: QueryResult => List[MatchResult[IndexedSeq[String]]] = { result =>
161161
val columns = result.rows.get.columnNames
162-
List(columns must contain(allOf("id", "some_bytes")).inOrder, columns must have size(2))
162+
List(columns must contain(allOf("id", "some_bytes")).inOrder, columns must have size (2))
163163
}
164164

165-
val ideasMatcher : QueryResult => List[MatchResult[IndexedSeq[String]]] = { result =>
165+
val ideasMatcher: QueryResult => List[MatchResult[IndexedSeq[String]]] = { result =>
166166
val columns = result.rows.get.columnNames
167-
List(columns must contain(allOf("id", "some_idea")).inOrder, columns must have size(2))
167+
List(columns must contain(allOf("id", "some_idea")).inOrder, columns must have size (2))
168168
}
169169

170170
withConnection {
@@ -204,10 +204,10 @@ class QuerySpec extends Specification with ConnectionHelper {
204204
executeQuery(connection, insert)
205205

206206
val rows = executeQuery(connection, select).rows.get
207-
rows(0)("bit_column") === Array(0,0,-128)
207+
rows(0)("bit_column") === Array(0, 0, -128)
208208

209209
val preparedRows = executePreparedStatement(connection, select).rows.get
210-
preparedRows(0)("bit_column") === Array(0,0,-128)
210+
preparedRows(0)("bit_column") === Array(0, 0, -128)
211211
}
212212

213213
}
@@ -264,6 +264,30 @@ class QuerySpec extends Specification with ConnectionHelper {
264264

265265
}
266266

267+
"select from a large text column" in {
268+
269+
val create = "create temporary table bombs (id char(4), bomb mediumtext character set ascii)"
270+
271+
val insert = """ insert bombs values
272+
| ('bomb', repeat(' ',65536+16384+8192+4096+2048+1024+512+256+128)),
273+
| ('good', repeat(' ',65536+16384+8192+4096+2048+1024+512+256+128-1))""".stripMargin
274+
275+
276+
withConnection {
277+
connection =>
278+
executeQuery(connection, create)
279+
executeQuery(connection, insert)
280+
val result = executeQuery(connection, "select bomb from bombs").rows.get
281+
282+
result.size === 2
283+
284+
result(0)("bomb").asInstanceOf[String].length === 98176
285+
result(1)("bomb").asInstanceOf[String].length === 98175
286+
}
287+
288+
}
289+
290+
267291
}
268292

269293
}

0 commit comments

Comments
 (0)
0