8000 Fixing issue with zeroed dates in MySQL - fixes #93 · mauricio/postgresql-async@3523144 · GitHub
[go: up one dir, main page]

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

Commit 3523144

Browse files
committed
Fixing issue with zeroed dates in MySQL - fixes #93
1 parent 10a7351 commit 3523144

File tree

11 files changed

+96
-24
lines changed

11 files changed

+96
-24
lines changed

db-async-common/src/main/scala/com/github/mauricio/async/db/column/DateEncoderDecoder.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@ import com.github.mauricio.async.db.exceptions.DateEncoderNotAvailableException
2222

2323
object DateEncoderDecoder extends ColumnEncoderDecoder {
2424

25+
private val ZeroedDate = "0000-00-00"
26+
2527
private val formatter = DateTimeFormat.forPattern("yyyy-MM-dd")
2628

27-
override def decode(value: String): LocalDate = {
28-
this.formatter.parseLocalDate(value)
29-
}
29+
override def decode(value: String): LocalDate =
30+
if ( ZeroedDate == value ) {
< 10000 /td>31+
null
32+
} else {
33+
this.formatter.parseLocalDate(value)
34+
}
3035

3136
override def encode(value: Any): String = {
3237
value match {

db-async-common/src/main/scala/com/github/mauricio/async/db/column/LocalDateTimeEncoderDecoder.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import org.joda.time.LocalDateTime
2121

2222
object LocalDateTimeEncoderDecoder extends ColumnEncoderDecoder {
2323

24+
private val ZeroedTimestamp = "0000-00-00 00:00:00"
25+
2426
private val optional = new DateTimeFormatterBuilder()
2527
.appendPattern(".SSSSSS").toParser
2628

@@ -33,5 +35,10 @@ object LocalDateTimeEncoderDecoder extends ColumnEncoderDecoder {
3335
format.print(value.asInstanceOf[LocalDateTime])
3436

3537
override def decode(value: String): LocalDateTime =
36-
format.parseLocalDateTime(value)
38+
if (ZeroedTimestamp == value) {
39+
null
40+
} else {
41+
format.parseLocalDateTime(value)
42+
}
43+
3744
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,13 @@ import io.netty.buffer.ByteBuf
2020
import org.joda.time.LocalDate
2121

2222
object DateDecoder extends BinaryDecoder {
23-
override def decode(buffer: ByteBuf): LocalDate = TimestampDecoder.decode(buffer).toLocalDate
23+
override def decode(buffer: ByteBuf): LocalDate = {
24+
val result = TimestampDecoder.decode(buffer)
25+
26+
if ( result != null ) {
27+
result.toLocalDate
28+
} else {
29+
null
30+
}
31+
}
2432
}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ object TimestampDecoder extends BinaryDecoder {
2424
val size = buffer.readUnsignedByte()
2525

2626
size match {
27-
case 0 => LocalDateTime.now()
28-
.withDate(0, 0, 0)
29-
.withTime(0, 0, 0, 0)
27+
case 0 => null
3028
case 4 => new LocalDateTime()
3129
.withDate(buffer.readUnsignedShort(), buffer.readUnsignedByte(), buffer.readUnsignedByte())
3230
.withTime(0, 0, 0, 0)

mysql-async/src/test/resources/logback.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</encoder>
1414
</appender>
1515

16-
<root level="TRACE">
16+
<root level="INFO">
1717
<appender-ref ref="STDOUT"/>
1818
<appender-ref ref="FILE"/>
1919
</root>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ class QuerySpec extends Specification with ConnectionHelper {
181181
matcher(executeQuery(connection, select))
182182
ideasMatcher(executeQuery(connection, selectIdeas))
183183

184+
success("completed")
184185
}
185186

186187
F438 }

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.github.mauricio.async.db.mysql
22

33
import org.specs2.mutable.Specification
4-
import com.github.mauricio.async.db.util.ExecutorServiceUtils._
54
import com.github.mauricio.async.db.util.FutureUtils.awaitFuture
65
import com.github.mauricio.async.db.mysql.exceptions.MySQLException
76
import com.github.mauricio.async.db.Connection
@@ -48,7 +47,7 @@ class TransactionSpec extends Specification with ConnectionHelper {
4847

4948
try {
5049
awaitFuture(future)
51-
ko("Should not have arrived here")
50+
failure("should not have arrived here")
5251
} catch {
5352
case e : MySQLException => {
5453

@@ -58,7 +57,7 @@ class TransactionSpec extends Specification with ConnectionHelper {
5857
val result = executePreparedStatement(connection, this.select).rows.get
5958
result.size === 1
6059
result(0)("name") === "Maurício Aragão"
61-
ok("success")
60+
success("correct result")
6261
}
6362
}
6463
}
@@ -83,14 +82,14 @@ class TransactionSpec extends Specification with ConnectionHelper {
8382

8483
try {
8584
awaitFuture(future)
86-
ko("this should not be reached")
85+
failure("this should not be reached")
8786
} catch {
8887
case e : MySQLException => {
8988

9089
pool.availables must have size(0)
9190
pool.availables must not contain(connection.asInstanceOf[MySQLConnection])
9291

93-
ok("success")
92+
success("success")
9493
}
9594
}
9695

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.github.mauricio.async.db.mysql
2+
3+
import org.specs2.mutable.Specification
4+
import scala.concurrent.duration.Duration
5+
import com.github.mauricio.async.db.RowData
6+
7+
class ZeroDatesSpec extends Specification with ConnectionHelper {
8+
9+
val createStatement =
10+
"""CREATE TEMPORARY TABLE dates (
11+
|`name` varchar (255) NOT NULL,
12+
|`timestamp_column` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
13+
|`date_column` date NOT NULL DEFAULT '0000-00-00',
14+
|`datetime_column` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
15+
|`time_column` time NOT NULL DEFAULT '00:00:00',
16+
|`year_column` year NOT NULL DEFAULT '0000'
17+
|)
18+
|ENGINE=MyISAM DEFAULT CHARSET=utf8;""".stripMargin
19+
20+
val insertStatement = "INSERT INTO dates (name) values ('Joe')"
21+
val selectStatement = "SELECT * FROM dates"
22+
23+
def matchValues( result : RowData ) = {
24+
result("name") === "Joe"
25+
result("timestamp_column") must beNull
26+
result("datetime_column") must beNull
27+
result("date_column") must beNull
28+
result("year_column") === 0
29+
result("time_column") === Duration.Zero
30+
}
31+
32+
"client" should {
33+
34+
"correctly parse the MySQL zeroed dates as NULL values in text protocol" in {
35+
36+
withConnection {
37+
connection =>
38+
executeQuery(connection, createStatement)
39+
executeQuery(connection, insertStatement)
40+
41+
matchValues(executeQuery(connection, selectStatement).rows.get(0))
42+
}
43+
}
44+
45+
"correctly parse the MySQL zeroed dates as NULL values in binary protocol" in {
46+
47+
withConnection {
48+
connection =>
49+
executeQuery(connection, createStatement)
50+
executeQuery(connection, insertStatement)
51+
52+
matchValues(executePreparedStatement(connection, selectStatement).rows.get(0))
53+
}
54+
}
55+
56+
}
57+
58+
}

postgresql-async/src/test/scala/com/github/mauricio/async/db/postgresql/PreparedStatementSpec.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ class PreparedStatementSpec extends Specification with DatabaseTestHelper {
116116
executeDdl(handler, this.messagesCreate)
117117
executeDdl(handler, create)
118118

119-
1.until(4).map {
119+
foreach(1.until(4)) {
120120
x =>
121121
executePreparedStatement(handler, this.messagesInsert, Array(message, moment))
122122
executePreparedStatement(handler, insert, Array(otherMoment, otherMessage))
@@ -132,7 +132,6 @@ class PreparedStatementSpec extends Specification with DatabaseTestHelper {
132132
otherResult.columnNames must contain(allOf("id", "other_moment", "other_content")).inOrder
133133
otherResult(x - 1)("other_moment") === otherMoment
134134
otherResult(x - 1)("other_content") === otherMessage
135-
136135
}
137136

138137
}

postgresql-async/src/test/scala/com/github/mauricio/async/db/postgresql/TimeAndDateSpec.scala

A458
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class TimeAndDateSpec extends Specification with DatabaseTestHelper {
128128

129129
"support timestamp with timezone and microseconds" in {
130130

131-
1.until(6).inclusive.map {
131+
foreach(1.until(6)) {
132132
index =>
133133
withHandler {
134134
handler =>
@@ -156,8 +156,6 @@ class TimeAndDateSpec extends Specification with DatabaseTestHelper {
156156
dateTime.getMillis must be_>=(915779106000L)
157157
dateTime.getMillis must be_<(915779107000L)
158158
}
159-
160-
161159
}
162160
}
163161

0 commit comments

Comments
 (0)
0