@@ -80,12 +80,35 @@ function readCodeFor(field, config, options, fieldNum) {
80
80
81
81
function compile ( fields , options , config ) {
82
82
const parserFn = genFunc ( ) ;
83
- let i = 0 ;
84
83
const nullBitmapLength = Math . floor ( ( fields . length + 7 + 2 ) / 8 ) ;
85
84
86
- /* eslint-disable no-trailing-spaces */
87
- /* eslint-disable no-spaced-func */
88
- /* eslint-disable no-unexpected-multiline */
85
+ function wrap ( field , packet ) {
86
+ return {
87
+ type : typeNames [ field . columnType ] ,
88
+ length : field . columnLength ,
89
+ db : field . schema ,
90
+ table : field . table ,
91
+ name : field . name ,
92
+ string : function ( encoding = field . encoding ) {
93
+ if ( field . columnType === Types . JSON && encoding === field . encoding ) {
94
+ // Since for JSON columns mysql always returns charset 63 (BINARY),
95
+ // we have to handle it according to JSON specs and use "utf8",
96
+ // see https://github.com/sidorares/node-mysql2/issues/1661
97
+ console . warn (
98
+ `typeCast: JSON column "${ field . name } " is interpreted as BINARY by default, recommended to manually set utf8 encoding: \`field.string("utf8")\`` ,
99
+ ) ;
100
+ }
101
+
102
+ return packet . readLengthCodedString ( encoding ) ;
103
+ } ,
104
+ buffer : function ( ) {
105
+ return packet . readLengthCodedBuffer ( ) ;
106
+ } ,
107
+ geometry : function ( ) {
108
+ return packet . parseGeometryValue ( ) ;
109
+ } ,
110
+ } ;
111
+ }
89
112
90
113
parserFn ( '(function(){' ) ;
91
114
parserFn ( 'return class BinaryRow {' ) ;
@@ -96,24 +119,19 @@ function compile(fields, options, config) {
96
119
if ( options . rowsAsArray ) {
97
120
parserFn ( `const result = new Array(${ fields . length } );` ) ;
98
121
} else {
99
- parserFn ( " const result = {};" ) ;
122
+ parserFn ( ' const result = {};' ) ;
100
123
}
101
124
102
- const resultTables = { } ;
103
- let resultTablesArray = [ ] ;
104
-
105
- if ( options . nestTables === true ) {
106
- for ( i = 0 ; i < fields . length ; i ++ ) {
107
- resultTables [ fields [ i ] . table ] = 1 ;
108
- }
109
- resultTablesArray = Object . keys ( resultTables ) ;
110
- for ( i = 0 ; i < resultTablesArray . length ; i ++ ) {
111
- parserFn ( `result[${ helpers . srcEscape ( resultTablesArray [ i ] ) } ] = {};` ) ;
112
- }
125
+ // Global typeCast
126
+ if (
127
+ typeof config . typeCast === 'function' &&
128
+ typeof options . typeCast !== 'function'
129
+ ) {
130
+ options . typeCast = config . typeCast ;
113
131
}
114
132
115
133
parserFn ( 'packet.readInt8();' ) ; // status byte
116
- for ( i = 0 ; i < nullBitmapLength ; ++ i ) {
134
+ for ( let i = 0 ; i < nullBitmapLength ; ++ i ) {
117
135
parserFn ( `const nullBitmaskByte${ i } = packet.readInt8();` ) ;
118
136
}
119
137
@@ -123,38 +141,44 @@ function compile(fields, options, config) {
123
141
let fieldName = '' ;
124
142
let tableName = '' ;
125
143
126
- for ( i = 0 ; i < fields . length ; i ++ ) {
144
+ for ( let i = 0 ; i < fields . length ; i ++ ) {
127
145
fieldName = helpers . srcEscape ( fields [ i ] . name ) ;
128
146
parserFn ( `// ${ fieldName } : ${ typeNames [ fields [ i ] . columnType ] } ` ) ;
129
147
130
148
if ( typeof options . nestTables === 'string' ) {
131
- tableName = helpers . srcEscape ( fields [ i ] . table ) ;
132
149
lvalue = `result[${ helpers . srcEscape (
133
- fields [ i ] . table + options . nestTables + fields [ i ] . name
150
+ fields [ i ] . table + options . nestTables + fields [ i ] . name ,
134
151
) } ]`;
135
152
} else if ( options . nestTables === true ) {
136
153
tableName = helpers . srcEscape ( fields [ i ] . table ) ;
154
+ parserFn ( `if (!result[${ tableName } ]) result[${ tableName } ] = {};` ) ;
137
155
lvalue = `result[${ tableName } ][${ fieldName } ]` ;
138
156
} else if ( options . rowsAsArray ) {
139
157
lvalue = `result[${ i . toString ( 10 ) } ]` ;
140
158
} else {
141
- lvalue = `result[${ helpers . srcEscape ( fields [ i ] . name ) } ]` ;
159
+ lvalue = `result[${ fieldName } ]` ;
160
+ }
161
+
162
+ if ( options . typeCast === false ) {
163
+ parserFn ( `${ lvalue } = packet.readLengthCodedBuffer();` ) ;
164
+ } else {
165
+ const fieldWrapperVar = `fieldWrapper${ i } ` ;
166
+ parserFn ( `const ${ fieldWrapperVar } = wrap(fields[${ i } ], packet);` ) ;
167
+ const readCode = readCodeFor ( fields [ i ] , config , options , i ) ;
168
+
169
+ parserFn ( `if (nullBitmaskByte${ nullByteIndex } & ${ currentFieldNullBit } )` ) ;
170
+ parserFn ( `${ lvalue } = null;` ) ;
171
+ parserFn ( 'else {' ) ;
172
+ if ( typeof options . typeCast === 'function' ) {
173
+ parserFn (
174
+ `${ lvalue } = options.typeCast(${ fieldWrapperVar } , function() { return ${ readCode } });` ,
175
+ ) ;
176
+ } else {
177
+ parserFn ( `${ lvalue } = ${ readCode } ;` ) ;
178
+ }
179
+ parserFn ( '}' ) ;
142
180
}
143
181
144
- // TODO: this used to be an optimisation ( if column marked as NOT_NULL don't include code to check null
145
- // bitmap at all, but it seems that we can't rely on this flag, see #178
146
- // TODO: benchmark performance difference
147
- //
148
- // if (fields[i].flags & FieldFlags.NOT_NULL) { // don't need to check null bitmap if field can't be null.
149
- // result.push(lvalue + ' = ' + readCodeFor(fields[i], config));
150
- // } else if (fields[i].columnType == Types.NULL) {
151
- // result.push(lvalue + ' = null;');
152
- // } else {
153
- parserFn ( `if (nullBitmaskByte${ nullByteIndex } & ${ currentFieldNullBit } )` ) ;
154
- parserFn ( `${ lvalue } = null;` ) ;
155
- parserFn ( 'else' ) ;
156
- parserFn ( `${ lvalue } = ${ readCodeFor ( fields [ i ] , config , options , i ) } ` ) ;
157
- // }
158
182
currentFieldNullBit *= 2 ;
159
183
if ( currentFieldNullBit === 0x100 ) {
160
184
currentFieldNullBit = 1 ;
@@ -166,17 +190,13 @@ function compile(fields, options, config) {
166
190
parserFn ( '}' ) ;
167
191
parserFn ( '};' ) ( '})()' ) ;
168
192
169
- /* eslint-enable no-trailing-spaces */
170
- /* eslint-enable no-spaced-func */
171
- /* eslint-enable no-unexpected-multiline */
172
-
173
193
if ( config . debug ) {
174
194
helpers . printDebugWithCode (
175
195
'Compiled binary protocol row parser' ,
176
- parserFn . toString ( )
196
+ parserFn . toString ( ) ,
177
197
) ;
178
198
}
179
- return parserFn . toFunction ( ) ;
199
+ return parserFn . toFunction ( { wrap } ) ;
180
200
}
181
201
182
202
function getBinaryParser ( fields , options , config ) {
0 commit comments