解析Node.js MySQL查询返回的fields对象差异及还原方法
问题解析与解决方案
一、结构差异的原因
命令行打印的fields是经过MySQL客户端库(如mysql/mysql2)格式化后的友好展示结果,而直接返回JSON的是库内部的原始字段对象:
- 当你用
console.log打印时,Node.js会调用对象的util.inspect方法,客户端库为了可读性,对字段对象做了自定义的inspect逻辑——隐藏了_buf、_catalogStart等内部缓冲、偏移量属性,只展示解析后的友好字段信息(如orgName、flags数组、typeName)。 - 而
JSON.stringify只会序列化对象的可枚举属性,客户端库并未将格式化后的友好属性设置为可枚举,反而把内部处理用的私有属性(带下划线的那些)暴露了出来,导致JSON结果和控制台打印的结构完全不同。
二、恢复友好结构的方法
你可以通过手动转换字段对象,提取需要的属性并解析flags,也可以利用客户端库的内置工具来处理:
方法1:手动编写转换函数(通用所有MySQL客户端库)
// 格式化单个字段对象 function formatField(field) { // 从缓冲中提取字符串(对应orgName、orgTable等属性) const getBufStr = (start, length) => field._buf.toString('utf8', start, start + length); const formatted = { catalog: 'def', // MySQL catalog通常固定为def schema: getBufStr(field._schemaStart, field._schemaLength), name: field.name, orgName: getBufStr(field._orgNameStart, field._orgNameLength), table: getBufStr(field._tableStart, field._tableLength), orgTable: getBufStr(field._orgTableStart, field._orgTableLength), characterSet: field.characterSet, encoding: field.encoding, columnLength: field.columnLength, type: field.type, decimals: field.decimals, typeName: getTypeName(field.type), flags: parseFieldFlags(field.flags) }; return formatted; } // 映射MySQL类型码到类型名称 function getTypeName(typeCode) { const typeMap = { 253: 'VAR_STRING', 3: 'INT', 12: 'DATE', 1: 'TINYINT', 2: 'SMALLINT', // 更多类型码可参考MySQL官方文档补充 }; return typeMap[typeCode] || 'UNKNOWN_TYPE'; } // 解析flags数字为可读字符串数组 function parseFieldFlags(flagsNum) { const flagMap = { 1: 'NOT NULL', 2: 'PRIMARY KEY', 4: 'UNIQUE', 32: 'UNSIGNED', 64: 'ZEROFILL', 512: 'AUTO_INCREMENT', 4096: 'NO DEFAULT VALUE', // 更多flags位对应关系参考MySQL协议文档 }; const flags = []; for (const [bit, name] of Object.entries(flagMap)) { if (flagsNum & parseInt(bit)) { flags.push(name); } } return flags; } // 在查询回调中使用 db.query("SELECT * FROM build_stat", function (err, result, fields) { if (err) { throw err; } const formattedFields = fields.map(formatField); response.json({ "result": result, "fields": formattedFields }); });
方法2:利用mysql2库的内置常量(推荐)
如果你使用的是mysql2,它内置了与MySQL协议一致的常量,可以更准确地解析类型和flags:
const mysql = require('mysql2'); const { FieldFlags, FieldTypes } = mysql.constants; function formatField(field) { return { catalog: 'def', schema: field.schema, name: field.name, orgName: field.orgName, table: field.table, orgTable: field.orgTable, characterSet: field.characterSet, encoding: field.encoding, columnLength: field.columnLength, type: field.type, decimals: field.decimals, typeName: FieldTypes[field.type], flags: parseFieldFlags(field.flags) }; } function parseFieldFlags(flags) { const flagsList = []; for (const [name, bit] of Object.entries(FieldFlags)) { if (flags & bit) { flagsList.push(name.replace(/_/g, ' ')); } } return flagsList; }
三、关于flags: 4097的解析
flags的数字是位掩码,每个二进制位对应一个字段属性。4097的二进制是1000000000001,对应两个位:
- 第0位(值为1):
NOT NULL - 第12位(值为4096):
NO DEFAULT VALUE
通过位运算flagsNum & 位值可以判断该属性是否存在,最终组合成可读的字符串数组。
内容的提问来源于stack exchange,提问作者user22009348
相关产品推荐
相关产品推荐

