8000 Fixes issue with LocalDateTime not being printed - fixes #73 · mauricio/postgresql-async@69e29d8 · 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 69e29d8

Browse files
committed
Fixes issue with LocalDateTime not being printed - fixes #73
1 parent 9e8bc86 commit 69e29d8

File tree

14 files changed

+137
-36
lines changed

14 files changed

+137
-36
lines changed

mysql-async/src/main/java/com/github/mauricio/async/db/mysql/MySQLHelper.java renamed to db-async-common/src/main/java/com/github/mauricio/async/db/util/BufferDumper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
package com.github.mauricio.async.db.mysql;
1+
package com.github.mauricio.async.db.util;
22

33
import io.netty.buffer.ByteBuf;
44

5-
public class MySQLHelper {
5+
public class BufferDumper {
66

77
public static final String dumpAsHex(ByteBuf buffer) {
88
int length = buffer.readableBytes();

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

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,32 @@ import org.joda.time._
2323
import org.joda.time.format.DateTimeFormatterBuilder
2424

2525
object TimestampEncoderDecoder {
26+
val BaseFormat = "yyyy-MM-dd HH:mm:ss"
27+
val MillisFormat = ".SSSSSS"
2628
val Instance = new TimestampEncoderDecoder()
2729
}
2830

2931
class TimestampEncoderDecoder extends ColumnEncoderDecoder {
3032

33+
import TimestampEncoderDecoder._
34+
3135
private val optional = new DateTimeFormatterBuilder()
32-
.appendPattern(".SSSSSS").toParser
36+
.appendPattern(MillisFormat).toParser
3337
private val optionalTimeZone = new DateTimeFormatterBuilder()
3438
.appendPattern("Z").toParser
3539

36-
private val format = new DateTimeFormatterBuilder()
37-
.appendPattern("yyyy-MM-dd HH:mm:ss")
40+
private val builder = new DateTimeFormatterBuilder()
41+
.appendPattern(BaseFormat)
3842
.appendOptional(optional)
3943
.appendOptional(optionalTimeZone)
40-
.toFormatter
44+
45+
private val timezonedPrinter = new DateTimeFormatterBuilder()
46+
.appendPattern(s"${BaseFormat}${MillisFormat}Z").toFormatter
47+
48+
private val nonTimezonedPrinter = new DateTimeFormatterBuilder()
49+
.appendPattern(s"${BaseFormat}${MillisFormat}").toFormatter
50+
51+
private val format = builder.toFormatter
4152

4253
def formatter = format
4354

@@ -47,11 +58,11 @@ class TimestampEncoderDecoder extends ColumnEncoderDecoder {
4758

4859
override def encode(value: Any): String = {
4960
value match {
50-
case t: Timestamp => this.formatter.print(new DateTime(t))
51-
case t: Date => this.formatter.print(new DateTime(t))
52-
case t: Calendar => this.formatter.print(new DateTime(t))
53-
case t: LocalDateTime => this.formatter.print(t)
54-
case t: ReadableDateTime => this.formatter.print(t)
61+
case t: Timestamp => this.timezonedPrinter.print(new DateTime(t))
62+
case t: Date => this.timezonedPrinter.print(new DateTime(t))
63+
case t: Calendar => this.timezonedPrinter.print(new DateTime(t))
64+
case t: LocalDateTime => this.nonTimezonedPrinter.print(t)
65+
case t: ReadableDateTime => this.timezonedPrinter.print(t)
5566
case _ => throw new DateEncoderNotAvailableException(value)
5667
}
5768
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2013 Maurício Linhares
3+
*
4+
* Maurício Linhares licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.github.mauricio.async.db.column
18+
19+
import org.specs2.mutable.Specification
20+
import org.joda.time.DateTime
21+
import java.sql.Timestamp
22+
import org.joda.time.format.DateTimeFormatterBuilder
23+
import java.util.Calendar
24+
25+
class TimestampEncoderDecoderSpec extends Specification {
26+
27+
val encoder = TimestampEncoderDecoder.Instance
28+
val dateTime = new DateTime()
29+
.withDate(2013, 12, 27)
30+
.withTime(8, 40, 50, 800)
31+
32+
val result = "2013-12-27 08:40:50.800000"
33+
val formatter = new DateTimeFormatterBuilder().appendPattern("Z").toFormatter
34+
val resultWithTimezone = s"2013-12-27 08:40:50.800000${formatter.print(dateTime)}"
35+
36+
"decoder" should {
37+
38+
"should print a timestamp" in {
39+
val timestamp = new Timestamp(dateTime.toDate.getTime)
40+
encoder.encode(timestamp) === resultWithTimezone
41+
}
42+
43+
"should print a LocalDateTime" in {
44+
encoder.encode(dateTime.toLocalDateTime) === result
45+
}
46+
47+
"should print a date" in {
48+
encoder.encode(dateTime.toDate) === resultWithTimezone
49+
}
50+
51+
"should print a calendar" in {
52+
val calendar = Calendar.getInstance()
53+
calendar.setTime(dateTime.toDate)
54+
encoder.encode(calendar) === resultWithTimezone
55+
}
56+
57+
"should print a datetime" in {
58+
encoder.encode(dateTime) === resultWithTimezone
59+
}
60+
61+
}
62+
63+
}

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,9 @@ package com.github.mauricio.async.db.mysql.binary
1818

1919
import _root_.io.netty.buffer.ByteBuf
2020
import com.github.mauricio.async.db.exceptions.BufferNotFullyConsumedException
21-
import com.github.mauricio.async.db.mysql.binary.decoder._
22-
import com.github.mauricio.async.db.mysql.column.ColumnTypes
2321
import com.github.mauricio.async.db.mysql.message.server.ColumnDefinitionMessage
2422
import com.github.mauricio.async.db.util._
25-
import java.nio.charset.Charset
2623
import scala.collection.mutable.ArrayBuffer
27-
import com.github.mauricio.async.db.mysql.MySQLHelper
28-
import scala.annotation.switch
29-
import com.github.mauricio.async.db.mysql.util.CharsetMapper
3024

3125
object BinaryRowDecoder {
3226
final val log = Log.get[BinaryRowDecoder]

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/codec/MySQLConnectionHandler.scala

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ class MySQLConnectionHandler(
187187

188188
def write( message : QueryMessage ) : ChannelFuture = {
189189
this.decoder.queryProcessStarted()
190-
this.currentContext.writeAndFlush(message)
190+
writeAndHandleError(message)
191191
}
192192

193193
def write( message : PreparedStatementMessage ) {
@@ -203,17 +203,17 @@ class MySQLConnectionHandler(
203203
}
204204
case None => {
205205
decoder.preparedStatementPrepareStarted()
206-
this.currentContext.writeAndFlush( new PreparedStatementPrepareMessage(message.statement) )
206+
writeAndHandleError( new PreparedStatementPrepareMessage(message.statement) )
207207
}
208208
}
209209
}
210210

211211
def write( message : HandshakeResponseMessage ) : ChannelFuture = {
212-
this.currentContext.writeAndFlush(message)
212+
writeAndHandleError(message)
213213
}
214214

215215
def write( message : QuitMessage ) : ChannelFuture = {
216-
this.currentContext.writeAndFlush(message)
216+
writeAndHandleError(message)
217217
}
218218

219219
def disconnect: ChannelFuture = this.currentContext.close()
@@ -236,7 +236,7 @@ class MySQLConnectionHandler(
236236
decoder.preparedStatementExecuteStarted(columnsCount, parameters.size)
237237
this.currentColumns.clear()
238238
this.currentParameters.clear()
239-
this.currentContext.writeAndFlush(new PreparedStatementExecuteMessage( statementId, values, parameters ))
239+
writeAndHandleError(new PreparedStatementExecuteMessage( statementId, values, parameters ))
240240
}
241241

242242
private def onPreparedStatementPrepareResponse( message : PreparedStatementPrepareResponse ) {
@@ -266,4 +266,14 @@ class MySQLConnectionHandler(
266266
}
267267
}
268268

269+
private def writeAndHandleError( message : Any ) : ChannelFuture = {
270+
val future = this.currentContext.writeAndFlush(message)
271+
272+
future.onFailure {
273+
case e : Throwable => handleException(e)
274+
}
275+
276+
future
277+
}
278+
269279
}

mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/codec/MySQLFrameDecoder.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,13 @@ import com.github.mauricio.async.db.mysql.decoder._
2121
import com.github.mauricio.async.db.mysql.message.server._
2222
import com.github.mauricio.async.db.util.ByteBufferUtils.read3BytesInt
2323
import com.github.mauricio.async.db.util.ChannelWrapper.bufferToWrapper
24-
import com.github.mauricio.async.db.util.Log
24+
import com.github.mauricio.async.db.util.{BufferDumper, Log}
2525
import io.netty.buffer.ByteBuf
2626
import io.netty.channel.ChannelHandlerContext
2727
import io.netty.handler.codec.ByteToMessageDecoder
2828
import java.nio.ByteOrder
2929
import java.nio.charset.Charset
3030
import java.util.concurrent.atomic.AtomicInteger
31-
import com.github.mauricio.async.db.mysql.MySQLHelper
3231

3332

3433
class MySQLFrameDecoder(charset: Charset, connectionId : String) extends ByteToMessageDecoder {

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@
1717
package com.github.mauricio.async.db.mysql.decoder
1818

1919
import com.github.mauricio.async.db.mysql.message.server.{PreparedStatementPrepareResponse, ServerMessage}
20-
import com.github.mauricio.async.db.util.Log
20+
import com.github.mauricio.async.db.util.{BufferDumper, Log}
2121
import io.netty.buffer.ByteBuf
22-
import com.github.mauricio.async.db.mysql.MySQLHelper
2322

2423
class PreparedStatementPrepareResponseDecoder extends MessageDecoder {
2524

postgresql-async/src/main/scala/com/github/mauricio/async/db/postgresql/codec/MessageEncoder.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import com.github.mauricio.async.db.exceptions.EncoderNotAvailableException
2121
import com.github.mauricio.async.db.postgresql.encoders._
2222
import com.github.mauricio.async.db.postgresql.messages.backend.ServerMessage
2323
import com.github.mauricio.async.db.postgresql.messages.frontend._
24-
import com.github.mauricio.async.db.util.Log
24+
import com.github.mauricio.async.db.util.{BufferDumper, Log}
2525
import java.nio.charset.Charset
2626
import scala.annotation.switch
2727
import io.netty.handler.codec.MessageToMessageEncoder
@@ -43,7 +43,7 @@ class MessageEncoder(charset: Charset, encoderRegistry: ColumnEncoderRegistry) e
4343

4444
val buffer = msg match {
4545
case message: ClientMessage => {
46-
val encoder = (message.kind : @switch) match {
46+
val encoder = (message.kind: @switch) match {
4747
case ServerMessage.Close => CloseMessageEncoder
4848
case ServerMessage.Execute => this.executeEncoder
4949
case ServerMessage.Parse => this.openEncoder
@@ -52,7 +52,9 @@ class MessageEncoder(charset: Charset, encoderRegistry: ColumnEncoderRegistry) e
5252
case ServerMessage.PasswordMessage => this.credentialEncoder
5353
case _ => throw new EncoderNotAvailableException(message)
5454
}
55+
5556
encoder.encode(message)
57+
5658
}
5759
case _ => {
5860
throw new IllegalArgumentException("Can not encode message %s".format(msg))

postgresql-async/src/main/scala/com/github/mauricio/async/db/postgresql/codec/PostgreSQLConnectionHandler.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,9 @@ class PostgreSQLConnectionHandler
206206
}
207207

208208
def write( message : ClientMessage ) {
209-
this.currentContext.writeAndFlush(message)
209+
this.currentContext.writeAndFlush(message).onFailure {
210+
case e : Throwable => connectionDelegate.onError(e)
211+
}
210212
}
211213

212214
}

postgresql-async/src/main/scala/com/github/mauricio/async/db/postgresql/column/PostgreSQLColumnEncoderRegistry.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import com.github.mauricio.async.db.column._
2020
import org.joda.time._
2121
import scala.Some
2222
import scala.collection.JavaConversions._
23-
import java.nio.charset.Charset
2423

2524
object PostgreSQLColumnEncoderRegistry {
2625
val Instance = new PostgreSQLColumnEncoderRegistry()
@@ -51,7 +50,7 @@ class PostgreSQLColumnEncoderRegistry extends ColumnEncoderRegistry {
5150
classOf[java.math.BigDecimal] -> (BigDecimalEncoderDecoder -> ColumnTypes.Numeric),
5251

5352
classOf[LocalDate] -> ( DateEncoderDecoder -> ColumnTypes.Date ),
54-
classOf[LocalDateTime] -> (TimestampEncoderDecoder.Instance -> ColumnTypes.TimestampWithTimezone),
53+
classOf[LocalDateTime] -> (TimestampEncoderDecoder.Instance -> ColumnTypes.Timestamp),
5554
classOf[DateTime] -> (TimestampWithTimezoneEncoderDecoder -> ColumnTypes.TimestampWithTimezone),
5655
classOf[ReadableDateTime] -> (TimestampWithTimezoneEncoderDecoder -> ColumnTypes.TimestampWithTimezone),
5756
classOf[ReadableInstant] -> (DateEncoderDecoder -> ColumnTypes.Date),

postgresql-async/src/main/scala/com/github/mauricio/async/db/postgresql/encoders/PreparedStatementOpeningEncoder.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package com.github.mauricio.async.db.postgresql.encoders
1919
import com.github.mauricio.async.db.column.ColumnEncoderRegistry
2020
import com.github.mauricio.async.db.postgresql.messages.backend.ServerMessage
2121
import com.github.mauricio.async.db.postgresql.messages.frontend.{ClientMessage, PreparedStatementOpeningMessage}
22-
import com.github.mauricio.async.db.util.ByteBufferUtils
22+
import com.github.mauricio.async.db.util.{Log, ByteBufferUtils}
2323
import java.nio.charset.Charset
2424
import io.netty.buffer.{Unpooled, ByteBuf}
2525

@@ -28,6 +28,8 @@ class PreparedStatementOpeningEncoder(charset: Charset, encoder : ColumnEncoderR
2828
with PreparedStatementEncoderHelper
2929
{
3030

31+
private val log = Log.get[PreparedStatementOpeningEncoder]
32+
3133
override def encode(message: ClientMessage): ByteBuf = {
3234

3335
val m = message.asInstanceOf[PreparedStatementOpeningMessage]

postgresql-async/src/main/scala/com/github/mauricio/async/db/postgresql/messages/frontend/PreparedStatementOpeningMessage.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,9 @@ import com.github.mauricio.async.db.column.ColumnEncoderRegistry
2020
import com.github.mauricio.async.db.postgresql.messages.backend.ServerMessage
2121

2222
class PreparedStatementOpeningMessage(statementId: Int, query: String, values: Seq[Any], encoderRegistry : ColumnEncoderRegistry)
23-
extends PreparedStatementMessage(statementId: Int, ServerMessage.Parse, query, values, encoderRegistry)
23+
extends PreparedStatementMessage(statementId: Int, ServerMessage.Parse, query, values, encoderRegistry) {
24+
25+
override def toString() : String =
26+
s"${this.getClass.getSimpleName}(id=${statementId},query=${query},values=${values}})"
27+
28+
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import concurrent.{Future, Await}
2727
import org.specs2.mutable.Specification
2828
import scala.concurrent.ExecutionContext.Implicits.global
2929
import scala.concurrent.duration._
30+
import org.joda.time.LocalDateTime
3031

3132
object PostgreSQLConnectionSpec {
3233
val log = Log.get[PostgreSQLConnectionSpec]
@@ -413,6 +414,20 @@ class PostgreSQLConnectionSpec extends Specification with DatabaseTestHelper {
413414

414415
}
415416

417+
"insert a LocalDateTime" in {
418+
419+
withHandler {
420+
handler =>
421+
executePreparedStatement(handler, "CREATE TEMP TABLE test(t TIMESTAMP)")
422+
val date1 = new LocalDateTime
423+
executePreparedStatement(handler, "INSERT INTO test(t) VALUES(?)", Array(date1))
424+
val result = executePreparedStatement(handler, "SELECT t FROM test")
425+
val date2 = result.rows.get.head(0)
426+
date1 === date2
427+
}
428+
429+
}
430+
416431
}
417432

418433
}

project/Build.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ object Configuration {
5454
val commonDependencies = Seq(
5555
"commons-pool" % "commons-pool" % "1.6",
5656
"org.slf4j" % "slf4j-api" % "1.7.5",
57-
"joda-time" % "joda-time" % "2.2",
58-
"org.joda" % "joda-convert" % "1.3.1",
57+
"joda-time" % "joda-time" % "2.3",
58+
"org.joda" % "joda-convert" % "1.5",
5959
"org.scala-lang" % "scala-library" % projectScalaVersion,
60-
"io.netty" % "netty-all" % "4.0.12.Final",
60+
"io.netty" % "netty-all" % "4.0.14.Final",
6161
"org.javassist" % "javassist" % "3.18.1-GA",
6262
specs2Dependency,
6363
logbackDependency

0 commit comments

Comments
 (0)
0