From 5eae85a8bed640c373b6250129c3360afb8c4f67 Mon Sep 17 00:00:00 2001 From: Willem van Bergen Date: Wed, 7 Sep 2011 06:59:14 -0400 Subject: [PATCH] Refactor types. --- src/backend_message.coffee | 47 +++----- src/query.coffee | 6 +- src/types.coffee | 109 +++++++++++++++++- src/value_decoders.coffee | 38 ------ src/vertica.coffee | 22 ++-- test/backend_message_test.coffee | 2 +- ...oders_test.coffee => decoders_test.coffee} | 36 +++--- 7 files changed, 151 insertions(+), 109 deletions(-) delete mode 100644 src/value_decoders.coffee rename test/{value_decoders_test.coffee => decoders_test.coffee} (52%) diff --git a/src/backend_message.coffee b/src/backend_message.coffee index 1966419..4015a88 100644 --- a/src/backend_message.coffee +++ b/src/backend_message.coffee @@ -1,4 +1,5 @@ -AuthenticationMethods = require('./authentication').methods +AuthenticationMethods = require('./authentication').methods +typeOIDs = require('./types').typeOIDs class BackendMessage typeId: null @@ -53,29 +54,6 @@ class BackendMessage.EmptyQueryResponse extends BackendMessage class BackendMessage.RowDescription extends BackendMessage typeId: 84 # T - fieldTypes: - 5: "boolean" - 6: "integer" - 7: "real" - 8: "string" - 9: "string" - 10: "date" - 11: "time" - 12: "timestamp" - 13: "timestamp" - 14: "interval" - 15: "time" - 16: "numeric" - 25: "string" - 1043: "string" - 20: "integer" - 21: "integer" - 23: "integer" - 26: "integer" - 700: "integer" - 701: "integer" - 1700: "real" - read: (buffer) -> numberOfFields = buffer.readUInt16(0) pos = 2 @@ -88,7 +66,7 @@ class BackendMessage.RowDescription extends BackendMessage pos += 4 tableFieldIndex = buffer.readUInt16(pos) pos += 2 - typeId = buffer.readUInt32(pos) + typeOID = buffer.readUInt32(pos) pos += 4 size = buffer.readUInt16(pos) pos += 2 @@ -102,12 +80,12 @@ class BackendMessage.RowDescription extends BackendMessage name: name tableId: tableId tableFieldIndex: tableFieldIndex - typeId: typeId - type: @fieldTypes[typeId] + typeOID: typeOID + type: typeOIDs[typeOID] size: size modifier: modifier formatCode: formatCode - + @columns.push fieldDescriptor @@ -139,6 +117,18 @@ class BackendMessage.CommandComplete extends BackendMessage @status = buffer.readZeroTerminatedString() +class BackendMessage.ParameterDescription extends BackendMessage + typeId: 116 # t + + read: (buffer) -> + count = buffer.readUInt16(0) + @types = (buffer.readUInt32(2 + i * 4) for i in [0 ... count]) + + +class BackendMessage.ParseComplete extends BackendMessage + typeId: 49 # 1 + + class BackendMessage.ErrorResponse extends BackendMessage typeId: 69 # E @@ -195,7 +185,6 @@ class BackendMessage.CopyInResponse extends BackendMessage pos += 1 - ############################################################## # BackendMessage factory ############################################################## diff --git a/src/query.coffee b/src/query.coffee index ee459ae..2dd4b8f 100644 --- a/src/query.coffee +++ b/src/query.coffee @@ -1,13 +1,13 @@ EventEmitter = require('events').EventEmitter FrontendMessage = require('./frontend_message') -ValueDecorders = require('./value_decoders') +valueDecoders = require('./types').decoders class Query extends EventEmitter constructor: (@connection, @sql, @callback) -> @_handlingCopyIn = false - + execute: () -> @emit 'start' @@ -133,7 +133,7 @@ class Query.Field @modifier = msg.modifier @formatCode = msg.formatCode - @convert = ValueDecorders[@formatCode][@type] || ValueDecorders[@formatCode].default + @convert = valueDecoders[@formatCode][@type] || valueDecoders[@formatCode].default module.exports = Query diff --git a/src/types.coffee b/src/types.coffee index 1749bd1..de1b889 100644 --- a/src/types.coffee +++ b/src/types.coffee @@ -1,10 +1,37 @@ - padWithZeroes = (str, length) -> res = "#{str}" res = "0#{res}" while res.length < length return res +exports.typeOIDs = + 5: "boolean" + 6: "integer" + 7: "real" + 8: "string" + 9: "string" + 10: "date" + 11: "time" + 12: "timestamp" + 13: "timestamp" + 14: "interval" + 15: "time" + 16: "numeric" + 25: "string" + 1043: "string" + 20: "integer" + 21: "integer" + 23: "integer" + 26: "integer" + 700: "integer" + 701: "integer" + 1700: "real" + + +################################################ +# Vertica Date type +################################################ + class VerticaDate constructor: (year, month, day) -> @year = +year @@ -27,6 +54,12 @@ VerticaDate.fromDate = (date) -> new VerticaDate(date.getFullYear(), date.getMonth() + 1, date.getDate()) +exports.Date = VerticaDate + +################################################ +# Vertica Time type +################################################ + class VerticaTime constructor: (hour, minute, second) -> @hour = +hour @@ -43,8 +76,51 @@ VerticaTime.fromStringBuffer = (buffer) -> else throw 'Invalid time format!' +exports.Time = VerticaTime + +################################################ +# Vertica Timestamp type +################################################ + +# not implemented as a separate class as of yet + +VerticaTimestamp = + + fromStringBuffer: (buffer) -> + timezoneOffset = require('./vertica') + timestampRegexp = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?(?:([\+\-])(\d{2})(?:\:(\d{2}))?)?$/ + if matches = buffer.toString('ascii').match(timestampRegexp) + utc = Date.UTC(+matches[1], +matches[2] - 1, +matches[3], +matches[4], +matches[5], +matches[6], Math.round(+matches[7] * 1000) || 0) + + if matches[8] + timezoneOffset = +matches[9] * 60 + (+matches[10] || 0) + timezoneOffset = 0 - timezoneOffset if matches[8] == '-' + utc -= timezoneOffset * 60 * 1000 + else if VerticaTimestamp.timezoneOffset + utc -= VerticaTimestamp.timezoneOffset -# class VerticaTimestamp + new Date(utc) + + else + throw 'Invalid timestamp string returned' + + + setTimezoneOffset: (offset) -> + if !offset? + VerticaTimestamp.timezoneOffset = null + else if matches = offset.match(/^([\+\-])(\d{1,2})(?:\:(\d{2}))?$/) + timezoneOffset = +matches[2] * 60 + (+matches[3] || 0) + timezoneOffset = 0 - timezoneOffset if matches[1] == '-' + VerticaTimestamp.timezoneOffset = timezoneOffset * 60 * 1000 + else + throw "Invalid timezone offset string: #{offset}!" + + +exports.Timestamp = VerticaTimestamp + +################################################ +# Vertica Interval type +################################################ class VerticaInterval constructor: (days, hours, minutes, seconds) -> @@ -87,7 +163,30 @@ VerticaInterval.fromStringBuffer = (buffer) -> throw 'Invalid interval format!' - -exports.Date = VerticaDate -exports.Time = VerticaTime exports.Interval = VerticaInterval + +################################################ +# value decoders +################################################ + +stringDecoders = + string: (buffer) -> buffer.toString() + integer: (buffer) -> +buffer + real: (buffer) -> parseFloat(buffer) + numeric: (buffer) -> parseFloat(buffer) + boolean: (buffer) -> buffer.toString() == 't' + date: (buffer) -> VerticaDate.fromStringBuffer(buffer) + time: (buffer) -> VerticaTime.fromStringBuffer(buffer) + interval: (buffer) -> VerticaInterval.fromStringBuffer(buffer) + timestamp: (buffer) -> VerticaTimestamp.fromStringBuffer(buffer) + default: (buffer) -> buffer.toString() + +binaryDecoders = + default: (buffer) -> throw 'Binary decoders not yet supported!' + + +exports.decoders = + 0: stringDecoders, + 1: binaryDecoders, + 'string': stringDecoders, + 'binary': binaryDecoders diff --git a/src/value_decoders.coffee b/src/value_decoders.coffee deleted file mode 100644 index 5cc7f73..0000000 --- a/src/value_decoders.coffee +++ /dev/null @@ -1,38 +0,0 @@ -Vertica = require('./vertica') - -stringDecoders = - string: (value) -> value.toString() - integer: (value) -> +value - real: (value) -> parseFloat(value) - numeric: (value) -> parseFloat(value) - boolean: (value) -> value.toString() == 't' - date: (value) -> Vertica.Date.fromStringBuffer(value) - time: (value) -> Vertica.Time.fromStringBuffer(value) - interval: (value) -> Vertica.Interval.fromStringBuffer(value) - - timestamp: (value) -> - timestampRegexp = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?(?:([\+\-])(\d{2})(?:\:(\d{2}))?)?$/ - if matches = value.toString('ascii').match(timestampRegexp) - utc = Date.UTC(+matches[1], +matches[2] - 1, +matches[3], +matches[4], +matches[5], +matches[6], Math.round(+matches[7] * 1000) || 0) - - if matches[8] - timezoneOffset = +matches[9] * 60 + (+matches[10] || 0) - timezoneOffset = 0 - timezoneOffset if matches[8] == '-' - utc -= timezoneOffset * 60 * 1000 - else if Vertica.timezoneOffset - utc -= Vertica.timezoneOffset - - new Date(utc) - - else - throw 'Invalid timestamp string returned' - - default: (value) -> value.toString() - - -binaryDecoders = - default: (value) -> - throw 'Binary decoders not yet supported!' - - -module.exports = 0: stringDecoders, 1: binaryDecoders, 'string': stringDecoders, 'binary': binaryDecoders diff --git a/src/vertica.coffee b/src/vertica.coffee index dc91be7..24cf62f 100644 --- a/src/vertica.coffee +++ b/src/vertica.coffee @@ -5,24 +5,16 @@ exports.connect = (connectionOptions, callback) -> connection.connect(callback) return connection -exports.setTimezoneOffset = (offset) -> - if !offset? - exports.timezoneOffset = null - else if matches = offset.match(/^([\+\-])(\d{1,2})(?:\:(\d{2}))?$/) - timezoneOffset = +matches[2] * 60 + (+matches[3] || 0) - timezoneOffset = 0 - timezoneOffset if matches[1] == '-' - exports.timezoneOffset = timezoneOffset * 60 * 1000 - else - throw "Invalid timezone offset string: #{offset}!" - types = require('./types') -quoting = require('./quoting') -exports.Date = types.Date -exports.Time = types.Time -# exports.Timestamp = types.Timestamp -exports.Interval = types.Interval +exports.Date = types.Date +exports.Time = types.Time +exports.Timestamp = types.Timestamp +exports.Interval = types.Interval +exports.setTimezoneOffset = types.Timestamp.setTimezoneOffset + +quoting = require('./quoting') exports.escape = quoting.escape exports.quote = quoting.quote diff --git a/test/backend_message_test.coffee b/test/backend_message_test.coffee index 62728eb..48a4f28 100644 --- a/test/backend_message_test.coffee +++ b/test/backend_message_test.coffee @@ -51,7 +51,7 @@ vow.addBatch assert.length message.columns, 1 assert.equal message.columns[0].tableId, 30110 assert.equal message.columns[0].tableFieldIndex, 1 - assert.equal message.columns[0].typeId, 6 + assert.equal message.columns[0].typeOID, 6 assert.equal message.columns[0].type, "integer" diff --git a/test/value_decoders_test.coffee b/test/decoders_test.coffee similarity index 52% rename from test/value_decoders_test.coffee rename to test/decoders_test.coffee index 6c1e349..7d678f3 100644 --- a/test/value_decoders_test.coffee +++ b/test/decoders_test.coffee @@ -1,10 +1,10 @@ vows = require 'vows' assert = require 'assert' -Vertica = require('../src/vertica') -ValueDecoders = require('../src/value_decoders') +types = require('../src/types') +decoders = require('../src/types').decoders -vow = vows.describe('Value Decoders') +vow = vows.describe('Decoding values') vow.addBatch 'string decoders': @@ -12,56 +12,56 @@ vow.addBatch 'timestamp with timezone': -> # Negative timezone data = new Buffer([50, 48, 49, 49, 45, 48, 56, 45, 50, 57, 32, 49, 55, 58, 51, 57, 58, 52, 53, 46, 54, 54, 53, 48, 53, 49, 45, 48, 50, 58, 51, 48]) - assert.deepEqual ValueDecoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 20, 09, 45, 665)) + assert.deepEqual decoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 20, 09, 45, 665)) # Positive timezone data = new Buffer([50, 48, 49, 49, 45, 48, 56, 45, 50, 57, 32, 50, 50, 58, 49, 50, 58, 52, 48, 46, 48, 51, 50, 50, 43, 48, 50]) - assert.deepEqual ValueDecoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 20, 12, 40, 32)) + assert.deepEqual decoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 20, 12, 40, 32)) # UTC = no offset data = new Buffer([50, 48, 49, 49, 45, 48, 56, 45, 50, 57, 32, 50, 48, 58, 49, 52, 58, 52, 53, 46, 54, 55, 49, 52, 52, 55, 43, 48, 48]) - assert.deepEqual ValueDecoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 20, 14, 45, 671)) + assert.deepEqual decoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 20, 14, 45, 671)) 'timestamp without timezone': -> # Use UTC by default data = new Buffer([50, 48, 49, 49, 45, 48, 56, 45, 50, 57, 32, 49, 55, 58, 51, 52, 58, 52, 48, 46, 53, 52, 54, 54, 48, 53]) - assert.deepEqual ValueDecoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 17, 34, 40, 547)) + assert.deepEqual decoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 17, 34, 40, 547)) # Set timezone offset to +2 - Vertica.setTimezoneOffset("+2") + types.Timestamp.setTimezoneOffset("+2") data = new Buffer([50, 48, 49, 49, 45, 48, 56, 45, 50, 57, 32, 49, 55, 58, 51, 52, 58, 52, 48, 46, 53, 52, 54, 54, 48, 53]) - assert.deepEqual ValueDecoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 15, 34, 40, 547)) - Vertica.setTimezoneOffset(null) + assert.deepEqual decoders.string.timestamp(data), new Date(Date.UTC(2011, 7, 29, 15, 34, 40, 547)) + types.Timestamp.setTimezoneOffset(null) 'date': -> data = new Buffer([50, 48, 49, 49, 45, 48, 56, 45, 50, 57]) - assert.deepEqual ValueDecoders.string.date(data), new Vertica.Date(2011, 8, 29) + assert.deepEqual decoders.string.date(data), new types.Date(2011, 8, 29) 'time': -> data = new Buffer([48, 52, 58, 48, 53, 58, 48, 54]) - assert.deepEqual ValueDecoders.string.time(data), new Vertica.Time(4,5,6) + assert.deepEqual decoders.string.time(data), new types.Time(4,5,6) 'string': -> data = new Buffer([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]) - assert.deepEqual ValueDecoders.string.string(data), 'hello world' + assert.deepEqual decoders.string.string(data), 'hello world' 'integer': -> data = new Buffer([49]) - assert.deepEqual ValueDecoders.string.integer(data), 1 + assert.deepEqual decoders.string.integer(data), 1 'real': -> data = new Buffer([49, 46, 51, 51]) - assert.deepEqual ValueDecoders.string.real(data), 1.33 + assert.deepEqual decoders.string.real(data), 1.33 'numeric': -> data = new Buffer([49, 48, 46, 53]) - assert.deepEqual ValueDecoders.string.real(data), 10.5 + assert.deepEqual decoders.string.real(data), 10.5 'boolean': -> - assert.deepEqual ValueDecoders.string.boolean(new Buffer([116])), true - assert.deepEqual ValueDecoders.string.boolean(new Buffer([102])), false + assert.deepEqual decoders.string.boolean(new Buffer([116])), true + assert.deepEqual decoders.string.boolean(new Buffer([102])), false vow.export(module)