Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Test case for issue #12

  • Loading branch information...
commit 8e65d58ddb3a7ad4aa591441a7a923132749d2b1 1 parent 2f850c1
@cretz cretz authored
View
84 lib/row.token.js
@@ -1,4 +1,4 @@
-var TdsUtils, Token,
+var TdsUtils, Token, _PLP_NULL,
__hasProp = Object.prototype.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
@@ -6,6 +6,8 @@ TdsUtils = require('./tds-utils').TdsUtils;
Token = require('./token').Token;
+_PLP_NULL = new Buffer([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
+
/**
Token for ROW (0xD1)
@@ -27,7 +29,7 @@ exports.RowToken = (function(_super) {
}
RowToken.prototype.fromBuffer = function(stream, context) {
- var column, index, val, _len, _ref, _results;
+ var chunk, chunkLength, chunks, column, index, len, pos, top, val, _i, _len, _len2, _ref, _results;
this._context = context;
this.metadata = context.colmetadata;
this.values = new Array(this.metadata.columns.length);
@@ -36,25 +38,74 @@ exports.RowToken = (function(_super) {
for (index = 0, _len = _ref.length; index < _len; index++) {
column = _ref[index];
val = {};
- switch (column.lengthType) {
- case 'int32LE':
- val.length = stream.readInt32LE();
- break;
- case 'uint16LE':
- val.length = stream.readUInt16LE();
- break;
- case 'uint8':
- val.length = stream.readByte();
- break;
- default:
- val.length = column.length;
+ context.debug('Checking column: ', column);
+ if (column.type.hasTextPointer) {
+ len = stream.readByte();
+ context.debug('Got len: ', len);
+ context.debug('Offset, length', stream.currentOffset(), stream.getBuffer().length);
+ if (len !== 0) {
+ stream.skip(len + 8);
+ } else {
+ val.length = -1;
+ }
+ context.debug('Val: ', val);
+ }
+ if (column.length !== 0xFFFF && val.length !== -1) {
+ switch (column.lengthType) {
+ case 'int32LE':
+ val.length = stream.readInt32LE();
+ break;
+ case 'uint16LE':
+ val.length = stream.readUInt16LE();
+ break;
+ case 'uint8':
+ val.length = stream.readByte();
+ break;
+ default:
+ val.length = column.length;
+ }
+ } else if (val.length !== -1) {
+ switch (column.type.sqlType) {
+ case 'Char':
+ case 'VarChar':
+ case 'NChar':
+ case 'NVarChar':
+ case 'Binary':
+ case 'VarBinary':
+ top = stream.readBuffer(8);
+ if (top.equals(_PLP_NULL)) {
+ val.length = -1;
+ } else {
+ chunkLength = stream.readUInt32LE();
+ val.length = 0;
+ chunks = [];
+ while (chunkLength !== 0) {
+ val.length += chunkLength;
+ chunks.push(stream.readBuffer(chunkLength));
+ chunkLength = stream.readUInt32LE();
+ }
+ val.buffer = new Buffer(val.length);
+ pos = 0;
+ for (_i = 0, _len2 = chunks.length; _i < _len2; _i++) {
+ chunk = chunks[_i];
+ chunk.copy(val.buffer, pos, 0);
+ pos += chunk.length;
+ }
+ if (column.type.sqlType === 'NChar' || column.type.sqlType === 'NVarChar') {
+ val.length /= 2;
+ }
+ }
+ break;
+ default:
+ val.length = column.length;
+ }
}
- if (val.length === 0xFFFF) val.length = -1;
if (val.length === 0 && column.type.emptyPossible) {
val.buffer = new Buffer(0);
} else if (val.length > 0) {
val.buffer = stream.readBuffer(val.length);
}
+ context.debug('Got value: ', val);
_results.push(this.values[index] = val);
}
return _results;
@@ -117,6 +168,7 @@ exports.RowToken = (function(_super) {
break;
case 'Char':
case 'VarChar':
+ case 'Text':
if (val.length === -1) {
return null;
} else {
@@ -125,6 +177,7 @@ exports.RowToken = (function(_super) {
break;
case 'NChar':
case 'NVarChar':
+ case 'NText':
if (val.length === -1) {
return null;
} else {
@@ -133,6 +186,7 @@ exports.RowToken = (function(_super) {
break;
case 'Binary':
case 'VarBinary':
+ case 'Image':
if (col.length === -1) {
return null;
} else {
View
9 lib/tds-constants.js
@@ -61,7 +61,8 @@ exports.TdsConstants = (function() {
sqlType: 'Image',
lengthType: 'int32LE',
hasTableName: true,
- emptyPossible: true
+ emptyPossible: true,
+ hasTextPointer: true
},
0x23: {
name: 'TEXTTYPE',
@@ -69,7 +70,8 @@ exports.TdsConstants = (function() {
lengthType: 'int32LE',
hasCollation: true,
hasTableName: true,
- emptyPossible: true
+ emptyPossible: true,
+ hasTextPointer: true
},
0x24: {
name: 'GUIDTYPE',
@@ -200,7 +202,8 @@ exports.TdsConstants = (function() {
lengthType: 'int32LE',
hasCollation: true,
hasTableName: true,
- emptyPossible: true
+ emptyPossible: true,
+ hasTextPointer: true
},
0x68: {
name: 'BITNTYPE',
View
54 src/row.token.coffee
@@ -1,6 +1,8 @@
{TdsUtils} = require './tds-utils'
{Token} = require './token'
+_PLP_NULL = new Buffer [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
+
###*
Token for ROW (0xD1)
@@ -22,12 +24,43 @@ class exports.RowToken extends Token
@values = new Array(@metadata.columns.length)
for column, index in @metadata.columns
val = {}
- switch column.lengthType
- when 'int32LE' then val.length = stream.readInt32LE()
- when 'uint16LE' then val.length = stream.readUInt16LE()
- when 'uint8' then val.length = stream.readByte()
- else val.length = column.length
- if val.length is 0xFFFF then val.length = -1
+ # ignore pointer
+ if column.type.hasTextPointer
+ len = stream.readByte()
+ if len isnt 0 then stream.skip len + 8
+ else val.length = -1
+ context.debug 'Val: ', val
+ if column.length isnt 0xFFFF and val.length isnt -1
+ switch column.lengthType
+ when 'int32LE' then val.length = stream.readInt32LE()
+ when 'uint16LE' then val.length = stream.readUInt16LE()
+ when 'uint8' then val.length = stream.readByte()
+ else val.length = column.length
+ else if val.length isnt -1
+ # max length stuff
+ switch column.type.sqlType
+ when 'Char', 'VarChar', 'NChar', 'NVarChar', 'Binary', 'VarBinary'
+ # grab the entire buffer
+ top = stream.readBuffer 8
+ if top.equals _PLP_NULL then val.length = -1
+ else
+ # skip expected length validation for now
+ chunkLength = stream.readUInt32LE()
+ val.length = 0
+ chunks = []
+ while chunkLength isnt 0
+ val.length += chunkLength
+ chunks.push stream.readBuffer(chunkLength)
+ chunkLength = stream.readUInt32LE()
+ val.buffer = new Buffer val.length
+ pos = 0
+ for chunk in chunks
+ chunk.copy val.buffer, pos, 0
+ pos += chunk.length
+ # the length is half if it's unicode
+ if column.type.sqlType is 'NChar' or column.type.sqlType is 'NVarChar'
+ val.length /= 2
+ else val.length = column.length
if val.length is 0 and column.type.emptyPossible
val.buffer = new Buffer 0
else if val.length > 0
@@ -65,13 +98,13 @@ class exports.RowToken extends Token
if val.length is 0 then null
else TdsUtils.bigIntBufferToString val.buffer
# TODO RowVersion/TimeStamp
- when 'Char', 'VarChar'
+ when 'Char', 'VarChar', 'Text'
if val.length is -1 then null
else val.buffer.toString 'ascii', 0, val.length
- when 'NChar', 'NVarChar'
+ when 'NChar', 'NVarChar', 'NText'
if val.length is -1 then null
else val.buffer.toString 'ucs2', 0, val.length * 2
- when 'Binary', 'VarBinary'
+ when 'Binary', 'VarBinary', 'Image'
if col.length is -1 then null
else val.buffer
# TODO when 'SmallMoney'
@@ -153,4 +186,5 @@ class exports.RowToken extends Token
date
_readDate: (buffer) ->
- throw new Error 'Not implemented'
+ throw new Error 'Not implemented'
+
View
3  src/tds-constants.coffee
@@ -62,6 +62,7 @@ class exports.TdsConstants
lengthType: 'int32LE'
hasTableName: true
emptyPossible: true
+ hasTextPointer: true
0x23:
name: 'TEXTTYPE'
sqlType: 'Text'
@@ -69,6 +70,7 @@ class exports.TdsConstants
hasCollation: true
hasTableName: true
emptyPossible: true
+ hasTextPointer: true
0x24:
name: 'GUIDTYPE'
sqlType: 'UniqueIdentifier'
@@ -176,6 +178,7 @@ class exports.TdsConstants
hasCollation: true
hasTableName: true
emptyPossible: true
+ hasTextPointer: true
0x68:
name: 'BITNTYPE'
lengthSubstitutes:
View
31 test/node-tds.issue0012.test.coffee
@@ -0,0 +1,31 @@
+{TestConstants} = require './constants.test'
+{TestUtils} = require './utils.test'
+
+describe 'Statement', ->
+
+ describe '#execute', ->
+ conn = null
+ beforeEach ->
+ conn = TestUtils.newConnection()
+
+ afterEach ->
+ conn?.end()
+
+ it 'should handle varchar and nvarchar max properly', (alldone) ->
+ foundRow = false
+ handler =
+ error: (error) ->
+ alldone error
+ row: (row) ->
+ TestUtils.assertRow row, 'VarCharCol', 'Foo'
+ TestUtils.assertRow row, 'NVarCharCol', 'Bar'
+ foundRow = true
+ done: (done) ->
+ if foundRow
+ alldone()
+ else
+ alldone new Error('Did not find row')
+ conn.connect =>
+ # select a varchar and nvarchar max
+ stmt = conn.createStatement "SELECT CAST('Foo' AS VARCHAR(MAX)) AS VarCharCol, CAST('Bar' AS NVARCHAR(MAX)) AS NVarCharCol", null, handler
+ stmt.execute()
View
78 test/node-tds.vartext.test.coffee
@@ -0,0 +1,78 @@
+{TestConstants} = require './constants.test'
+{TestUtils} = require './utils.test'
+
+describe 'Statement', ->
+
+ describe '#execute', ->
+ conn = null
+ beforeEach ->
+ conn = TestUtils.newConnection()
+
+ afterEach ->
+ conn?.end()
+
+ it 'should handle text properly', (alldone) ->
+ rowFound = false
+ handler =
+ row: (row) ->
+ rowFound = true
+ TestUtils.assertRow row, 0, 'Hello'
+ TestUtils.assertRow row, 1, ''
+ TestUtils.assertRow row, 2, null
+ done: (done) ->
+ if rowFound then alldone()
+ else alldone new Error('No row found')
+ conn.handler = handler
+ sql =
+ '''
+ SELECT CAST('Hello' AS Text) AS TextType1,
+ CAST('' AS Text) AS TextType2,
+ CAST(NULL AS Text) AS TextType3
+ '''
+ conn.connect =>
+ stmt = conn.createStatement sql, null, handler
+ stmt.execute()
+
+ it 'should handle ntext properly', (alldone) ->
+ rowFound = false
+ handler =
+ row: (row) ->
+ rowFound = true
+ TestUtils.assertRow row, 0, 'Hello'
+ TestUtils.assertRow row, 1, ''
+ TestUtils.assertRow row, 2, null
+ done: (done) ->
+ if rowFound then alldone()
+ else alldone new Error('No row found')
+ conn.handler = handler
+ sql =
+ '''
+ SELECT CAST('Hello' AS NText) AS NTextType1,
+ CAST('' AS NText) AS NTextType2,
+ CAST(NULL AS NText) AS NTextType3
+ '''
+ conn.connect =>
+ stmt = conn.createStatement sql, null, handler
+ stmt.execute()
+
+ it 'should handle image properly', (alldone) ->
+ rowFound = false
+ handler =
+ row: (row) ->
+ rowFound = true
+ TestUtils.assertRow row, 0, new Buffer [0x01, 0x02, 0x03]
+ TestUtils.assertRow row, 1, new Buffer 0
+ TestUtils.assertRow row, 2, null
+ done: (done) ->
+ if rowFound then alldone()
+ else alldone new Error('No row found')
+ conn.handler = handler
+ sql =
+ '''
+ SELECT CAST(0x010203 AS Image) AS ImageType1,
+ CAST('' AS Image) AS ImageType2,
+ CAST(NULL AS Image) AS ImageType3
+ '''
+ conn.connect =>
+ stmt = conn.createStatement sql, null, handler
+ stmt.execute()
Please sign in to comment.
Something went wrong with that request. Please try again.