diff --git a/packages/tools/src/SqlDumper.ts b/packages/tools/src/SqlDumper.ts index b11ab0077..9302fa368 100644 --- a/packages/tools/src/SqlDumper.ts +++ b/packages/tools/src/SqlDumper.ts @@ -79,7 +79,12 @@ export class SqlDumper implements AlterProcessor { else if (_isDate(value)) this.putStringValue(new Date(value).toISOString()); else if (value?.type == 'Buffer' && _isArray(value?.data)) this.putByteArrayValue(value?.data); else if (value?.$binary?.base64) { - this.putByteArrayValue(Array.from(Buffer.from(value.$binary.base64, 'base64'))); + const binary = atob(value.$binary.base64); + const bytes = new Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + this.putByteArrayValue(bytes); } else if (value?.$bigint) this.putRaw(value?.$bigint); else if (_isPlainObject(value) || _isArray(value)) this.putStringValue(JSON.stringify(value)); diff --git a/packages/tools/src/stringTools.ts b/packages/tools/src/stringTools.ts index 65b227d17..e9470ac9f 100644 --- a/packages/tools/src/stringTools.ts +++ b/packages/tools/src/stringTools.ts @@ -51,6 +51,11 @@ export function base64ToHex(base64String) { return '0x' + hexString.toUpperCase(); }; +export function hexToBase64(hexString) { + const binaryString = hexString.match(/.{1,2}/g).map(byte => String.fromCharCode(parseInt(byte, 16))).join(''); + return btoa(binaryString); +} + export function parseCellValue(value, editorTypes?: DataEditorTypesBehaviour) { if (!_isString(value)) return value; @@ -62,9 +67,10 @@ export function parseCellValue(value, editorTypes?: DataEditorTypesBehaviour) { const mHex = value.match(/^0x([0-9a-fA-F][0-9a-fA-F])+$/); if (mHex) { return { - type: 'Buffer', - data: hexStringToArray(value.substring(2)), - }; + $binary: { + base64: hexToBase64(value.substring(2)) + } + } } } @@ -246,10 +252,11 @@ export function stringifyCellValue( } if (editorTypes?.parseHexAsBuffer) { - if (value?.type == 'Buffer' && _isArray(value.data)) { - return { value: '0x' + arrayToHexString(value.data), gridStyle: 'valueCellStyle' }; - } + // if (value?.type == 'Buffer' && _isArray(value.data)) { + // return { value: '0x' + arrayToHexString(value.data), gridStyle: 'valueCellStyle' }; + // } } + if (editorTypes?.parseObjectIdAsDollar) { if (value?.$oid) { switch (intent) { diff --git a/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js index ac4fd9e45..112a61a9e 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js +++ b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js @@ -24,6 +24,15 @@ function extractTediousColumns(columns, addDriverNativeColumn = false) { return res; } +function modifyRow(row, columns) { + columns.forEach((col) => { + if (Buffer.isBuffer(row[col.columnName])) { + row[col.columnName] = { $binary: { base64: Buffer.from(row[col.columnName]).toString('base64') } }; + } + }); + return row; +} + async function getDefaultAzureSqlToken() { const credential = new ManagedIdentityCredential(); const tokenResponse = await credential.getToken('https://database.windows.net/.default'); @@ -121,9 +130,12 @@ async function tediousQueryCore(dbhan, sql, options) { }); request.on('row', function (columns) { result.rows.push( - _.zipObject( - result.columns.map(x => x.columnName), - columns.map(x => x.value) + modifyRow( + _.zipObject( + result.columns.map(x => x.columnName), + columns.map(x => x.value) + ), + result.columns ) ); }); @@ -148,13 +160,17 @@ async function tediousReadQuery(dbhan, sql, structure) { currentColumns = extractTediousColumns(columns); pass.write({ __isStreamHeader: true, + engine: 'mssql@dbgate-plugin-mssql', ...(structure || { columns: currentColumns }), }); }); request.on('row', function (columns) { - const row = _.zipObject( - currentColumns.map(x => x.columnName), - columns.map(x => x.value) + const row = modifyRow( + _.zipObject( + currentColumns.map(x => x.columnName), + columns.map(x => x.value) + ), + currentColumns ); pass.write(row); }); @@ -204,12 +220,15 @@ async function tediousStream(dbhan, sql, options) { }); request.on('columnMetadata', function (columns) { currentColumns = extractTediousColumns(columns); - options.recordset(currentColumns); + options.recordset(currentColumns, { engine: 'mssql@dbgate-plugin-mssql' }); }); request.on('row', function (columns) { - const row = _.zipObject( - currentColumns.map(x => x.columnName), - columns.map(x => x.value) + const row = modifyRow( + _.zipObject( + currentColumns.map(x => x.columnName), + columns.map(x => x.value) + ), + currentColumns ); options.row(row); }); diff --git a/plugins/dbgate-plugin-mysql/src/backend/drivers.js b/plugins/dbgate-plugin-mysql/src/backend/drivers.js index 3df30c1d4..019934db2 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/drivers.js +++ b/plugins/dbgate-plugin-mysql/src/backend/drivers.js @@ -21,7 +21,7 @@ function extractColumns(fields) { return null; } -function transformRow(row, columns) { +function modifyRow(row, columns) { columns.forEach((col) => { if (Buffer.isBuffer(row[col.columnName])) { row[col.columnName] = { $binary: { base64: Buffer.from(row[col.columnName]).toString('base64') } }; @@ -106,7 +106,7 @@ const drivers = driverBases.map(driverBase => ({ dbhan.client.query(sql, function (error, results, fields) { if (error) reject(error); const columns = extractColumns(fields); - resolve({ rows: results && columns && results.map && results.map(row => transformRow(zipDataRow(row, columns), columns)), columns }); + resolve({ rows: results && columns && results.map && results.map(row => modifyRow(zipDataRow(row, columns), columns)), columns }); }); }); }, @@ -139,7 +139,7 @@ const drivers = driverBases.map(driverBase => ({ options.recordset(columns, { engine: driverBase.engine }); } else { if (columns) { - options.row(transformRow(zipDataRow(row, columns), columns)); + options.row(modifyRow(zipDataRow(row, columns), columns)); } } }; @@ -184,7 +184,7 @@ const drivers = driverBases.map(driverBase => ({ ...(structure || { columns }), }); }) - .on('result', row => pass.write(transformRow(zipDataRow(row, columns), columns))) + .on('result', row => pass.write(modifyRow(zipDataRow(row, columns), columns))) .on('end', () => pass.end()); return pass;