Skip to content

Commit afdb800

Browse files
author
Suraiya Hameed
committed
feat: returnvalue token parser for fixed length types
1 parent 93a1363 commit afdb800

File tree

6 files changed

+1023
-1
lines changed

6 files changed

+1023
-1
lines changed

src/dataTypes.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* @flow */
2+
3+
const type = {
4+
// FIXEDLENTYPE
5+
[0x1F]: {
6+
id: 0x1F,
7+
type: 'NULL',
8+
name: 'Null'
9+
},
10+
[0x30]: {
11+
id: 0x30,
12+
type: 'INT1',
13+
name: 'TinyInt'
14+
},
15+
[0x32]: {
16+
id: 0x32,
17+
type: 'BIT',
18+
name: 'Bit'
19+
},
20+
[0x34]: {
21+
id: 0x34,
22+
type: 'INT2',
23+
name: 'SmallInt'
24+
},
25+
[0x38]: {
26+
id: 0x38,
27+
type: 'INT4',
28+
name: 'Int'
29+
},
30+
[0x3A]: {
31+
id: 0x3A,
32+
type: 'DATETIM4',
33+
name: 'SmallDateTime'
34+
},
35+
[0x3B]: {
36+
id: 0x3B,
37+
type: 'FLT4',
38+
name: 'Real'
39+
},
40+
[0x3C]: {
41+
id: 0x3C,
42+
type: 'MONEY',
43+
name: 'Money'
44+
},
45+
[0x3D]: {
46+
id: 0x3D,
47+
type: 'DATETIME',
48+
name: 'DateTime'
49+
},
50+
[0x3E]: {
51+
id: 0x3E,
52+
type: 'FLT8',
53+
name: 'Float'
54+
},
55+
[0x7A]: {
56+
id: 0x7A,
57+
type: 'MONEY4',
58+
name: 'SmallMoney'
59+
},
60+
[0x7F]: {
61+
id: 0x7F,
62+
type: 'INT8',
63+
name: 'BigInt'
64+
},
65+
66+
//VARLENTYPE
67+
[0x26]: {
68+
id: 0x26,
69+
name: 'IntN',
70+
type: 'INTN',
71+
LengthOfDataLength: 1
72+
}
73+
};
74+
75+
const typeByName = {};
76+
var keys = Object.keys(type);
77+
for (const key of keys) {
78+
typeByName[type[key].name] = type[key];
79+
}
80+
81+
module.exports.TYPE = type;
82+
83+
// TODO: export typeByName as TYPES in index/tedious.js
84+
// module.exports.TYPE = typeByName;

src/reader.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ function nextToken(reader) {
2424
case 0xAD: return readLoginAckToken;
2525
case 0xA9: return readOrderToken;
2626
case 0x79: return readReturnStatus;
27+
case 0xAC: return readReturnValueToken;
2728
default:
2829
console.log(reader.buffer.slice(reader.position - 1));
2930
throw new Error('Unknown token type ' + type.toString(16));
@@ -37,15 +38,17 @@ const Reader = module.exports = class Reader extends Transform {
3738
position: number
3839
buffer: Buffer
3940
version: number
41+
options: ?any // assign connection.options
4042

4143
stash: Array<any>
4244

43-
constructor(version: 0x07000000 | 0x07010000 | 0x71000001 | 0x72090002 | 0x730A0003 | 0x730B0003 | 0x74000004) {
45+
constructor(version: 0x07000000 | 0x07010000 | 0x71000001 | 0x72090002 | 0x730A0003 | 0x730B0003 | 0x74000004, options: ?any) {
4446
super({ readableObjectMode: true });
4547

4648
this.buffer = Buffer.alloc(0);
4749
this.version = version;
4850
this.position = 0;
51+
this.options = options;
4952

5053
this.stash = [];
5154

@@ -76,6 +79,10 @@ const Reader = module.exports = class Reader extends Transform {
7679
return this.buffer.readUInt16LE(this.position + offset);
7780
}
7881

82+
readInt16LE(offset: number) : number {
83+
return this.buffer.readInt16LE(this.position + offset);
84+
}
85+
7986
readUInt32LE(offset: number) : number {
8087
return this.buffer.readUInt32LE(this.position + offset);
8188
}
@@ -93,6 +100,19 @@ const Reader = module.exports = class Reader extends Transform {
93100
return 4294967296 * this.buffer.readUInt32LE(this.position + 4) + this.buffer.readUInt32LE(this.position);
94101
}
95102

103+
readInt64LE(offset: number) {
104+
// TODO: This can overflow
105+
return 4294967296 * this.buffer.readInt32LE(this.position + 4) + (this.buffer[this.position + 4] & (0x80 === 0x80 ? 1 : -1)) * this.buffer.readUInt32LE(this.position);
106+
}
107+
108+
readFloatLE(offset: number) {
109+
return this.buffer.readFloatLE(this.position + offset);
110+
}
111+
112+
readDoubleLE(offset: number) {
113+
return this.buffer.readDoubleLE(this.position + offset);
114+
}
115+
96116
_transform(chunk: Buffer | string, encoding: string | null, callback: (error: ?Error) => void) {
97117
if (!(chunk instanceof Buffer)) {
98118
return callback(new Error('Expected Buffer'));
@@ -133,3 +153,4 @@ const readInfoErrorToken = require('./tokens/infoerror/read');
133153
const readLoginAckToken = require('./tokens/loginack/read');
134154
const readOrderToken = require('./tokens/order/read');
135155
const readReturnStatus = require('./tokens/returnStatus/read');
156+
const readReturnValueToken = require('./tokens/returnvalue/read');

src/tokens/returnvalue/index.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* @flow */
2+
3+
const Token = require('../../token');
4+
import type { TypeInfo } from '../../types';
5+
6+
class ReturnValueToken extends Token {
7+
paramOrdinal: ?number
8+
paramName: ?string
9+
status: ?number
10+
userType: ?number
11+
// TODO: parser flag
12+
flags: {
13+
nullable: ?boolean,
14+
caseSensitive: ?boolean,
15+
updateable: ?boolean,
16+
identity: ?boolean,
17+
computed: ?boolean,
18+
reservedODBC: ?boolean,
19+
fixedLenCLRType: ?boolean,
20+
encrypted: ?boolean
21+
}
22+
typeInfo: ?TypeInfo
23+
valueLength: ?number
24+
value: ?any
25+
26+
constructor() {
27+
super(0xAC);
28+
29+
this.paramOrdinal = undefined;
30+
this.paramName = undefined;
31+
this.status = undefined;
32+
this.userType = undefined;
33+
this.flags = {
34+
nullable: undefined,
35+
caseSensitive: undefined,
36+
updateable: undefined,
37+
identity: undefined,
38+
computed: undefined,
39+
reservedODBC: undefined,
40+
fixedLenCLRType: undefined,
41+
encrypted: undefined
42+
};
43+
this.typeInfo = undefined;
44+
this.valueLength = undefined;
45+
this.value = undefined;
46+
}
47+
}
48+
module.exports = ReturnValueToken;
49+
50+
ReturnValueToken.read = require('./read');

src/tokens/returnvalue/read.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/* @flow */
2+
3+
import type Reader from '../../reader';
4+
import type { TypeInfo } from '../../types';
5+
6+
function readReturnValueToken(reader: Reader) {
7+
const token = new ReturnValueToken();
8+
9+
let offset = 0;
10+
token.paramOrdinal = reader.readUInt16LE(offset);
11+
offset += 2;
12+
const paramLength = reader.readUInt8(offset) * 2;
13+
offset += 1;
14+
token.paramName = reader.readString('ucs2', offset, offset + paramLength);
15+
offset += paramLength;
16+
17+
token.status = reader.readUInt8(offset);
18+
offset += 1;
19+
reader.consumeBytes(offset);
20+
21+
reader.stash.push(token);
22+
return parseUserType;
23+
}
24+
25+
function parseUserType(reader: Reader) {
26+
if (reader.version < 0x72090002) {
27+
return parseUserType_7_0;
28+
} else {
29+
return parseUserType_7_2;
30+
}
31+
}
32+
33+
function parseUserType_7_0(reader: Reader) {
34+
if (!reader.bytesAvailable(2)) {
35+
return;
36+
}
37+
38+
const userType = reader.readUInt16LE(0);
39+
reader.consumeBytes(2);
40+
41+
const token: ReturnValueToken = reader.stash[reader.stash.length - 1];
42+
token.userType = userType;
43+
44+
return parseFlags;
45+
}
46+
47+
function parseUserType_7_2(reader: Reader) {
48+
if (!reader.bytesAvailable(4)) {
49+
return;
50+
}
51+
52+
const userType = reader.readUInt32LE(0);
53+
reader.consumeBytes(4);
54+
55+
const token: ReturnValueToken = reader.stash[reader.stash.length - 1];
56+
token.userType = userType;
57+
58+
return parseFlags;
59+
}
60+
61+
function parseFlags(reader: Reader) {
62+
if (reader.version < 0x72090002) {
63+
return parseFlags_7_0;
64+
} else if (reader.version < 0x74000004) {
65+
return parseFlags_7_2;
66+
} else {
67+
return parseFlags_7_4;
68+
}
69+
}
70+
71+
72+
function parseFlags_7_0(reader: Reader) {
73+
if (!reader.bytesAvailable(2)) {
74+
return;
75+
}
76+
77+
// TODO: Implement flag parsing
78+
const flags = reader.readUInt16LE(0); // eslint-disable-line no-unused-vars
79+
reader.consumeBytes(2);
80+
81+
return parseTypeInfo;
82+
}
83+
84+
function parseFlags_7_2(reader: Reader) {
85+
if (!reader.bytesAvailable(2)) {
86+
return;
87+
}
88+
89+
// TODO: Implement flag parsing
90+
const flags = reader.readUInt16LE(0); // eslint-disable-line no-unused-vars
91+
reader.consumeBytes(2);
92+
93+
return parseTypeInfo;
94+
}
95+
96+
function parseFlags_7_4(reader: Reader) {
97+
if (!reader.bytesAvailable(2)) {
98+
return;
99+
}
100+
101+
// TODO: Implement flag parsing
102+
const flags = reader.readUInt16LE(0); // eslint-disable-line no-unused-vars
103+
reader.consumeBytes(2);
104+
105+
return parseTypeInfo;
106+
}
107+
108+
function parseTypeInfo(reader: Reader) {
109+
return readTypeInfo(parseValue, reader);
110+
}
111+
112+
function parseValue(reader: Reader) {
113+
const typeInfo: TypeInfo = reader.stash.pop();
114+
const token: ReturnValueToken = reader.stash[reader.stash.length - 1];
115+
token.typeInfo = typeInfo;
116+
return valueParse(afterReadingValue, reader);
117+
}
118+
119+
function afterReadingValue(reader: Reader) {
120+
const token: ReturnValueToken = reader.stash.pop();
121+
reader.push(token);
122+
return reader.nextToken;
123+
}
124+
125+
module.exports = readReturnValueToken;
126+
const ReturnValueToken = require('.');
127+
const { readTypeInfo } = require('../../types');
128+
const { valueParse } = require('../../value-parser');

0 commit comments

Comments
 (0)