diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 704f081141..bdb7eeaaa2 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -24,6 +24,7 @@ jobs: 'mysql:8.0.18', 'mysql:8.0.22', 'mysql:8.0.33', + 'mysql:9.0.1', 'mysql:latest', ] use-compression: [0, 1] diff --git a/lib/packets/packet.js b/lib/packets/packet.js index ccf3a8458c..42241ae950 100644 --- a/lib/packets/packet.js +++ b/lib/packets/packet.js @@ -608,6 +608,16 @@ class Packet { return parseGeometry(); } + parseVector() { + const bufLen = this.readLengthCodedNumber(); + const vectorEnd = this.offset + bufLen; + const result = []; + while (this.offset < vectorEnd && this.offset < this.end) { + result.push(this.readFloat()); + } + return result; + } + parseDate(timezone) { const strLen = this.readLengthCodedNumber(); if (strLen === null) { diff --git a/lib/parsers/binary_parser.js b/lib/parsers/binary_parser.js index c80bf962b7..4eb0d3ae69 100644 --- a/lib/parsers/binary_parser.js +++ b/lib/parsers/binary_parser.js @@ -55,6 +55,8 @@ function readCodeFor(field, config, options, fieldNum) { return 'packet.readLengthCodedString("ascii");'; case Types.GEOMETRY: return 'packet.parseGeometryValue();'; + case Types.VECTOR: + return 'packet.parseVector()'; case Types.JSON: // Since for JSON columns mysql always returns charset 63 (BINARY), // we have to handle it according to JSON specs and use "utf8", diff --git a/lib/parsers/text_parser.js b/lib/parsers/text_parser.js index f66a185afe..deedadf205 100644 --- a/lib/parsers/text_parser.js +++ b/lib/parsers/text_parser.js @@ -59,6 +59,8 @@ function readCodeFor(type, charset, encodingExpr, config, options) { return 'packet.readLengthCodedString("ascii")'; case Types.GEOMETRY: return 'packet.parseGeometryValue()'; + case Types.VECTOR: + return 'packet.parseVector()'; case Types.JSON: // Since for JSON columns mysql always returns charset 63 (BINARY), // we have to handle it according to JSON specs and use "utf8", diff --git a/test/esm/integration/connection/test-vector.test.mjs b/test/esm/integration/connection/test-vector.test.mjs new file mode 100644 index 0000000000..52571c2b07 --- /dev/null +++ b/test/esm/integration/connection/test-vector.test.mjs @@ -0,0 +1,56 @@ +import { test, assert, describe } from 'poku'; +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); +const common = require('../../../common.test.cjs'); + +const sql = `SELECT TO_VECTOR("[1.05, -17.8, 32, 123.456]") as test`; +const expectedArray = [1.05, -17.8, 32, 123.456]; +const epsilon = 1e-6; + +const compareFloat = (a, b) => Math.abs((a - b) / a) < epsilon; +const compareFLoatsArray = (a, b) => a.every((v, i) => compareFloat(v, b[i])); + +(async () => { + const connection = common.createConnection().promise(); + + const mySqlVersion = await common.getMysqlVersion(connection); + + if (mySqlVersion.major < 9) { + console.log( + `Skipping the test, required mysql version is 9 and above, actual version is ${mySqlVersion.major}`, + ); + await connection.end(); + return; + } + + await test(async () => { + describe( + 'Execute PS with vector response is parsed correctly', + common.describeOptions, + ); + + const [_rows] = await connection.execute(sql); + assert.equal( + compareFLoatsArray(_rows[0].test, expectedArray), + true, + `${_rows[0].test} should be equal to ${expectedArray}`, + ); + }); + + await test(async () => { + describe( + 'Select returning vector is parsed correctly', + common.describeOptions, + ); + + const [_rows] = await connection.query(sql); + assert.equal( + compareFLoatsArray(_rows[0].test, expectedArray), + true, + `${_rows[0].test} should be equal to ${expectedArray}`, + ); + }); + + await connection.end(); +})(); diff --git a/test/esm/regressions/2052.test.mjs b/test/esm/regressions/2052.test.mjs index 3e86040623..56ab217540 100644 --- a/test/esm/regressions/2052.test.mjs +++ b/test/esm/regressions/2052.test.mjs @@ -104,7 +104,7 @@ test(async () => { if (major === 9) return false; if (major === 8 && minor === 4 && patch === 1) return false; - if (major === 8 && minor === 0 && patch === 38) return false; + if (major === 8 && minor === 0 && patch >= 38) return false; if (major > 8) { return true; @@ -125,7 +125,7 @@ test(async () => { async () => new Promise((resolve, reject) => { describe( - 'E2E Prepare result with number of parameters incorrectly reported by the server', + `E2E Prepare result with number of parameters incorrectly reported by the server`, common.describeOptions, );