@@ -80,12 +80,35 @@ function readCodeFor(field, config, options, fieldNum) {
8080
8181function compile ( fields , options , config ) {
8282 const parserFn = genFunc ( ) ;
83- let i = 0 ;
8483 const nullBitmapLength = Math . floor ( ( fields . length + 7 + 2 ) / 8 ) ;
8584
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+ }
89112
90113 parserFn ( '(function(){' ) ;
91114 parserFn ( 'return class BinaryRow {' ) ;
@@ -96,24 +119,19 @@ function compile(fields, options, config) {
96119 if ( options . rowsAsArray ) {
97120 parserFn ( `const result = new Array(${ fields . length } );` ) ;
98121 } else {
99- parserFn ( " const result = {};" ) ;
122+ parserFn ( ' const result = {};' ) ;
100123 }
101124
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 ;
113131 }
114132
115133 parserFn ( 'packet.readInt8();' ) ; // status byte
116- for ( i = 0 ; i < nullBitmapLength ; ++ i ) {
134+ for ( let i = 0 ; i < nullBitmapLength ; ++ i ) {
117135 parserFn ( `const nullBitmaskByte${ i } = packet.readInt8();` ) ;
118136 }
119137
@@ -123,38 +141,44 @@ function compile(fields, options, config) {
123141 let fieldName = '' ;
124142 let tableName = '' ;
125143
126- for ( i = 0 ; i < fields . length ; i ++ ) {
144+ for ( let i = 0 ; i < fields . length ; i ++ ) {
127145 fieldName = helpers . srcEscape ( fields [ i ] . name ) ;
128146 parserFn ( `// ${ fieldName } : ${ typeNames [ fields [ i ] . columnType ] } ` ) ;
129147
130148 if ( typeof options . nestTables === 'string' ) {
131- tableName = helpers . srcEscape ( fields [ i ] . table ) ;
132149 lvalue = `result[${ helpers . srcEscape (
133- fields [ i ] . table + options . nestTables + fields [ i ] . name
150+ fields [ i ] . table + options . nestTables + fields [ i ] . name ,
134151 ) } ]`;
135152 } else if ( options . nestTables === true ) {
136153 tableName = helpers . srcEscape ( fields [ i ] . table ) ;
154+ parserFn ( `if (!result[${ tableName } ]) result[${ tableName } ] = {};` ) ;
137155 lvalue = `result[${ tableName } ][${ fieldName } ]` ;
138156 } else if ( options . rowsAsArray ) {
139157 lvalue = `result[${ i . toString ( 10 ) } ]` ;
140158 } 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 ( '}' ) ;
142180 }
143181
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- // }
158182 currentFieldNullBit *= 2 ;
159183 if ( currentFieldNullBit === 0x100 ) {
160184 currentFieldNullBit = 1 ;
@@ -166,17 +190,13 @@ function compile(fields, options, config) {
166190 parserFn ( '}' ) ;
167191 parserFn ( '};' ) ( '})()' ) ;
168192
169- /* eslint-enable no-trailing-spaces */
170- /* eslint-enable no-spaced-func */
171- /* eslint-enable no-unexpected-multiline */
172-
173193 if ( config . debug ) {
174194 helpers . printDebugWithCode (
175195 'Compiled binary protocol row parser' ,
176- parserFn . toString ( )
196+ parserFn . toString ( ) ,
177197 ) ;
178198 }
179- return parserFn . toFunction ( ) ;
199+ return parserFn . toFunction ( { wrap } ) ;
180200}
181201
182202function getBinaryParser ( fields , options , config ) {
0 commit comments