Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support raw bytes as input & binary/blob/bit will be convert to Uint8Array #55

Merged
merged 4 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
support binary
  • Loading branch information
shiyuhang0 committed Feb 21, 2024
commit 54ee66adb527e577b70022b300f83c2b34b23e51
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,4 @@ dist
.tern-port

.idea
out
29 changes: 22 additions & 7 deletions integration-test/type.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { connect, Row, FullResult } from '../dist/index'
import { fetch } from 'undici'
import * as dotenv from 'dotenv'
import {uint8ArrayToHex} from "../src/format";

dotenv.config()
const databaseURL = process.env.DATABASE_URL
Expand All @@ -24,7 +25,7 @@ const multiDataTable = `
t_decimal DECIMAL(38, 19),
t_char CHAR,
t_varchar VARCHAR(10),
c_binary binary(16),
c_binary binary(3),
c_varbinary varbinary(16),
t_tinytext TINYTEXT,
t_text TEXT,
Expand Down Expand Up @@ -84,6 +85,9 @@ const nullResult = {
t_json: null
}

// binary: x'1520c5' is the hex of 'FSDF' decoded from base64 (1520c5 has 3 bytes)
// blob : assume tidb serverless decode them with utf8
// bit: b'01010101' convert to hex is 55 (85 in 10 base)
const insertSQL = `
INSERT INTO ${database}.${table}( t_tinyint, t_tinyint_unsigned, t_smallint, t_smallint_unsigned, t_mediumint
, t_mediumint_unsigned, t_int, t_int_unsigned, t_bigint, t_bigint_unsigned
Expand All @@ -94,7 +98,7 @@ INSERT INTO ${database}.${table}( t_tinyint, t_tinyint_unsigned, t_smallint, t_s
, t_enum,t_bit, t_set, t_json)
VALUES ( -128, 255, -32768, 65535, -8388608, 16777215, -2147483648, 1, -9223372036854775808, 18446744073709551615
, true, 123.456, 123.123, 123456789012.123456789012
, '测', '测试', x'89504E470D0A1A0A', x'89504E470D0A1A0A', '测试tinytext', '0', '测试mediumtext', '测试longtext'
, '测', '测试', x'1520c5', x'1520c5', '测试tinytext', '0', '测试mediumtext', '测试longtext'
, 'tinyblob', 'blob', 'mediumblob', 'longblob'
, '1977-01-01', '9999-12-31 23:59:59', '19731230153000', '23:59:59', '2154'
, 'enum2',b'01010101', 'a,b','{"a":1,"b":"2"}')
Expand All @@ -117,8 +121,8 @@ const fullTypeResult = {
t_decimal: '123456789012.1234567890120000000',
t_char: '测',
t_varchar: '测试',
c_binary: '�PNG\r\n\x1A\n\x00\x00\x00\x00\x00\x00\x00\x00',
c_varbinary: '�PNG\r\n\x1A\n',
c_binary: 'FSDF',
c_varbinary: 'FSDF',
t_tinytext: '测试tinytext',
t_text: '0',
t_mediumtext: '测试mediumtext',
Expand All @@ -134,7 +138,7 @@ const fullTypeResult = {
t_year: 2154,
t_enum: 'enum2',
t_set: 'a,b',
t_bit: '\x00\x00\x00\x00\x00\x00\x00U',
t_bit: '0x0000000000000055',
t_json: { a: 1, b: '2' }
}

Expand All @@ -156,11 +160,22 @@ describe('types', () => {
})

test('test all types', async () => {
const con = connect({ url: databaseURL, database: database, fetch, debug: true })
const con = connect({ url: databaseURL, database: database, fetch})
await con.execute(`delete from ${table}`)
await con.execute(insertSQL)
const rows = (await con.execute('select * from multi_data_type')) as Row[]
expect(rows.length).toEqual(1)
expect(JSON.stringify(rows[0])).toEqual(JSON.stringify(fullTypeResult))
// binary type returns Uint8Array, encode with base64
rows[0]['c_binary'] = Buffer.from(rows[0]['c_binary']).toString('base64')
rows[0]['c_varbinary'] = Buffer.from(rows[0]['c_varbinary']).toString('base64')
// blob type returns Uint8Array, encode with utf8
rows[0]['t_tinyblob']=Buffer.from(rows[0]['t_tinyblob']).toString()
rows[0]['t_blob']=Buffer.from(rows[0]['t_blob']).toString()
rows[0]['t_mediumblob']=Buffer.from(rows[0]['t_mediumblob']).toString()
rows[0]['t_longblob']=Buffer.from(rows[0]['t_longblob']).toString()
// bit type returns Uint8Array, get it with hex
rows[0]['t_bit']=uint8ArrayToHex(rows[0]['t_bit'])

expect(JSON.stringify(rows[0])).toEqual(JSON.stringify(fullTypeResult))
})
})
43 changes: 9 additions & 34 deletions src/decode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Decoders } from './config'
import { Field } from './index.js'
import {Decoders} from './config'
import {Field} from './index.js'

export function cast(field: Field, value: string | null, decoder: Decoders): any {
if (value === null) {
Expand Down Expand Up @@ -35,6 +35,7 @@ export function cast(field: Field, value: string | null, decoder: Decoders): any
case 'TEXT':
case 'MEDIUMTEXT':
case 'LONGTEXT':
case 'TINYTEXT':
case 'DATE':
case 'TIME':
case 'DATETIME':
Expand All @@ -46,45 +47,19 @@ export function cast(field: Field, value: string | null, decoder: Decoders): any
case 'LONGBLOB':
case 'BINARY':
case 'VARBINARY':
case 'TINYTEXT':
case 'BIT':
return uint8Array(value)
return hexToUint8Array(value)
case 'JSON':
return JSON.parse(value)
default:
return value
}
}

export function uint8Array(text: string): Uint8Array {
return Uint8Array.from(bytes(text))
}

function bytes(text: string): number[] {
return text.split('').map((c) => c.charCodeAt(0))
}

function str2UTF8(str) {
var bytes = new Array();
var len, c;
len = str.length;
for (var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if (c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if (c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if (c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
function hexToUint8Array(hexString: string): Uint8Array {
const uint8Array = new Uint8Array(hexString.length / 2);
for (let i = 0; i < hexString.length; i += 2) {
uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
}
return bytes;
return uint8Array;
}
1 change: 0 additions & 1 deletion src/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ function sanitize(value: Value): string {
}

if (value instanceof Uint8Array) {
console.log("input convert to:"+uint8ArrayToHex(value))
return uint8ArrayToHex(value)
}

Expand Down
31 changes: 0 additions & 31 deletions test.js

This file was deleted.

Loading