328 lines
11 KiB
JavaScript
328 lines
11 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
const ResultSet = require('./resultset');
|
||
|
|
||
|
class CommonBinary extends ResultSet {
|
||
|
constructor(resolve, reject, cmdOpts, connOpts, sql, values) {
|
||
|
super(resolve, reject);
|
||
|
this.configAssign(connOpts, cmdOpts);
|
||
|
this.sql = sql;
|
||
|
this.initialValues = values;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write (and escape) current parameter value to output writer
|
||
|
*
|
||
|
* @param out output writer
|
||
|
* @param value current parameter
|
||
|
* @param opts connection options
|
||
|
* @param info connection information
|
||
|
*/
|
||
|
writeParam(out, value, opts, info) {
|
||
|
let flushed = false;
|
||
|
switch (typeof value) {
|
||
|
case 'boolean':
|
||
|
flushed = out.writeInt8(0x00);
|
||
|
flushed = out.writeInt8(value ? 0x01 : 0x00) || flushed;
|
||
|
break;
|
||
|
case 'bigint':
|
||
|
case 'number':
|
||
|
flushed = out.writeInt8(0x00);
|
||
|
flushed = out.writeLengthStringAscii('' + value) || flushed;
|
||
|
break;
|
||
|
case 'object':
|
||
|
if (Object.prototype.toString.call(value) === '[object Date]') {
|
||
|
flushed = out.writeInt8(0x00);
|
||
|
flushed = out.writeBinaryDate(value, opts) || flushed;
|
||
|
} else if (Buffer.isBuffer(value)) {
|
||
|
flushed = out.writeInt8(0x00);
|
||
|
flushed = out.writeLengthEncodedBuffer(value) || flushed;
|
||
|
} else if (typeof value.toSqlString === 'function') {
|
||
|
flushed = out.writeInt8(0x00);
|
||
|
flushed = out.writeLengthEncodedString(String(value.toSqlString())) || flushed;
|
||
|
} else {
|
||
|
if (
|
||
|
value.type != null &&
|
||
|
[
|
||
|
'Point',
|
||
|
'LineString',
|
||
|
'Polygon',
|
||
|
'MultiPoint',
|
||
|
'MultiLineString',
|
||
|
'MultiPolygon',
|
||
|
'GeometryCollection'
|
||
|
].includes(value.type)
|
||
|
) {
|
||
|
const geoBuff = this.getBufferFromGeometryValue(value);
|
||
|
if (geoBuff) {
|
||
|
flushed = out.writeInt8(0x00); //Value follow
|
||
|
flushed =
|
||
|
out.writeLengthEncodedBuffer(Buffer.concat([Buffer.from([0, 0, 0, 0]), geoBuff])) ||
|
||
|
flushed;
|
||
|
} else {
|
||
|
flushed = out.writeInt8(0x01); //NULL
|
||
|
}
|
||
|
} else {
|
||
|
//TODO check if permitSetMultiParamEntries is needed !?
|
||
|
flushed = out.writeInt8(0x00);
|
||
|
flushed = out.writeLengthEncodedString(JSON.stringify(value)) || flushed;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
flushed = out.writeInt8(0x00);
|
||
|
flushed = out.writeLengthEncodedString(value) || flushed;
|
||
|
}
|
||
|
return flushed;
|
||
|
}
|
||
|
|
||
|
getBufferFromGeometryValue(value, headerType) {
|
||
|
let geoBuff;
|
||
|
let pos;
|
||
|
let type;
|
||
|
if (!headerType) {
|
||
|
switch (value.type) {
|
||
|
case 'Point':
|
||
|
geoBuff = Buffer.allocUnsafe(21);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(1, 1); //wkbPoint
|
||
|
if (
|
||
|
value.coordinates &&
|
||
|
Array.isArray(value.coordinates) &&
|
||
|
value.coordinates.length >= 2 &&
|
||
|
!isNaN(value.coordinates[0]) &&
|
||
|
!isNaN(value.coordinates[1])
|
||
|
) {
|
||
|
geoBuff.writeDoubleLE(value.coordinates[0], 5); //X
|
||
|
geoBuff.writeDoubleLE(value.coordinates[1], 13); //Y
|
||
|
return geoBuff;
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
case 'LineString':
|
||
|
if (value.coordinates && Array.isArray(value.coordinates)) {
|
||
|
const pointNumber = value.coordinates.length;
|
||
|
geoBuff = Buffer.allocUnsafe(9 + 16 * pointNumber);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(2, 1); //wkbLineString
|
||
|
geoBuff.writeInt32LE(pointNumber, 5);
|
||
|
for (let i = 0; i < pointNumber; i++) {
|
||
|
if (
|
||
|
value.coordinates[i] &&
|
||
|
Array.isArray(value.coordinates[i]) &&
|
||
|
value.coordinates[i].length >= 2 &&
|
||
|
!isNaN(value.coordinates[i][0]) &&
|
||
|
!isNaN(value.coordinates[i][1])
|
||
|
) {
|
||
|
geoBuff.writeDoubleLE(value.coordinates[i][0], 9 + 16 * i); //X
|
||
|
geoBuff.writeDoubleLE(value.coordinates[i][1], 17 + 16 * i); //Y
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
return geoBuff;
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
case 'Polygon':
|
||
|
if (value.coordinates && Array.isArray(value.coordinates)) {
|
||
|
const numRings = value.coordinates.length;
|
||
|
let size = 0;
|
||
|
for (let i = 0; i < numRings; i++) {
|
||
|
size += 4 + 16 * value.coordinates[i].length;
|
||
|
}
|
||
|
geoBuff = Buffer.allocUnsafe(9 + size);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(3, 1); //wkbPolygon
|
||
|
geoBuff.writeInt32LE(numRings, 5);
|
||
|
pos = 9;
|
||
|
for (let i = 0; i < numRings; i++) {
|
||
|
const lineString = value.coordinates[i];
|
||
|
if (lineString && Array.isArray(lineString)) {
|
||
|
geoBuff.writeInt32LE(lineString.length, pos);
|
||
|
pos += 4;
|
||
|
for (let j = 0; j < lineString.length; j++) {
|
||
|
if (
|
||
|
lineString[j] &&
|
||
|
Array.isArray(lineString[j]) &&
|
||
|
lineString[j].length >= 2 &&
|
||
|
!isNaN(lineString[j][0]) &&
|
||
|
!isNaN(lineString[j][1])
|
||
|
) {
|
||
|
geoBuff.writeDoubleLE(lineString[j][0], pos); //X
|
||
|
geoBuff.writeDoubleLE(lineString[j][1], pos + 8); //Y
|
||
|
pos += 16;
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return geoBuff;
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
case 'MultiPoint':
|
||
|
type = 'MultiPoint';
|
||
|
geoBuff = Buffer.allocUnsafe(9);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(4, 1); //wkbMultiPoint
|
||
|
break;
|
||
|
|
||
|
case 'MultiLineString':
|
||
|
type = 'MultiLineString';
|
||
|
geoBuff = Buffer.allocUnsafe(9);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(5, 1); //wkbMultiLineString
|
||
|
break;
|
||
|
|
||
|
case 'MultiPolygon':
|
||
|
type = 'MultiPolygon';
|
||
|
geoBuff = Buffer.allocUnsafe(9);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(6, 1); //wkbMultiPolygon
|
||
|
break;
|
||
|
|
||
|
case 'GeometryCollection':
|
||
|
geoBuff = Buffer.allocUnsafe(9);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(7, 1); //wkbGeometryCollection
|
||
|
|
||
|
if (value.geometries && Array.isArray(value.geometries)) {
|
||
|
const coordinateLength = value.geometries.length;
|
||
|
const subArrays = [geoBuff];
|
||
|
for (let i = 0; i < coordinateLength; i++) {
|
||
|
const tmpBuf = this.getBufferFromGeometryValue(value.geometries[i]);
|
||
|
if (tmpBuf == null) break;
|
||
|
subArrays.push(tmpBuf);
|
||
|
}
|
||
|
geoBuff.writeInt32LE(subArrays.length - 1, 5);
|
||
|
return Buffer.concat(subArrays);
|
||
|
} else {
|
||
|
geoBuff.writeInt32LE(0, 5);
|
||
|
return geoBuff;
|
||
|
}
|
||
|
default:
|
||
|
return null;
|
||
|
}
|
||
|
if (value.coordinates && Array.isArray(value.coordinates)) {
|
||
|
const coordinateLength = value.coordinates.length;
|
||
|
const subArrays = [geoBuff];
|
||
|
for (let i = 0; i < coordinateLength; i++) {
|
||
|
const tmpBuf = this.getBufferFromGeometryValue(value.coordinates[i], type);
|
||
|
if (tmpBuf == null) break;
|
||
|
subArrays.push(tmpBuf);
|
||
|
}
|
||
|
geoBuff.writeInt32LE(subArrays.length - 1, 5);
|
||
|
return Buffer.concat(subArrays);
|
||
|
} else {
|
||
|
geoBuff.writeInt32LE(0, 5);
|
||
|
return geoBuff;
|
||
|
}
|
||
|
} else {
|
||
|
switch (headerType) {
|
||
|
case 'MultiPoint':
|
||
|
if (
|
||
|
value &&
|
||
|
Array.isArray(value) &&
|
||
|
value.length >= 2 &&
|
||
|
!isNaN(value[0]) &&
|
||
|
!isNaN(value[1])
|
||
|
) {
|
||
|
geoBuff = Buffer.allocUnsafe(21);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(1, 1); //wkbPoint
|
||
|
geoBuff.writeDoubleLE(value[0], 5); //X
|
||
|
geoBuff.writeDoubleLE(value[1], 13); //Y
|
||
|
return geoBuff;
|
||
|
}
|
||
|
return null;
|
||
|
|
||
|
case 'MultiLineString':
|
||
|
if (value && Array.isArray(value)) {
|
||
|
const pointNumber = value.length;
|
||
|
geoBuff = Buffer.allocUnsafe(9 + 16 * pointNumber);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(2, 1); //wkbLineString
|
||
|
geoBuff.writeInt32LE(pointNumber, 5);
|
||
|
for (let i = 0; i < pointNumber; i++) {
|
||
|
if (
|
||
|
value[i] &&
|
||
|
Array.isArray(value[i]) &&
|
||
|
value[i].length >= 2 &&
|
||
|
!isNaN(value[i][0]) &&
|
||
|
!isNaN(value[i][1])
|
||
|
) {
|
||
|
geoBuff.writeDoubleLE(value[i][0], 9 + 16 * i); //X
|
||
|
geoBuff.writeDoubleLE(value[i][1], 17 + 16 * i); //Y
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
return geoBuff;
|
||
|
}
|
||
|
return null;
|
||
|
|
||
|
case 'MultiPolygon':
|
||
|
if (value && Array.isArray(value)) {
|
||
|
const numRings = value.length;
|
||
|
let size = 0;
|
||
|
for (let i = 0; i < numRings; i++) {
|
||
|
size += 4 + 16 * value[i].length;
|
||
|
}
|
||
|
geoBuff = Buffer.allocUnsafe(9 + size);
|
||
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
||
|
geoBuff.writeInt32LE(3, 1); //wkbPolygon
|
||
|
geoBuff.writeInt32LE(numRings, 5);
|
||
|
pos = 9;
|
||
|
for (let i = 0; i < numRings; i++) {
|
||
|
const lineString = value[i];
|
||
|
if (lineString && Array.isArray(lineString)) {
|
||
|
geoBuff.writeInt32LE(lineString.length, pos);
|
||
|
pos += 4;
|
||
|
for (let j = 0; j < lineString.length; j++) {
|
||
|
if (
|
||
|
lineString[j] &&
|
||
|
Array.isArray(lineString[j]) &&
|
||
|
lineString[j].length >= 2 &&
|
||
|
!isNaN(lineString[j][0]) &&
|
||
|
!isNaN(lineString[j][1])
|
||
|
) {
|
||
|
geoBuff.writeDoubleLE(lineString[j][0], pos); //X
|
||
|
geoBuff.writeDoubleLE(lineString[j][1], pos + 8); //Y
|
||
|
pos += 16;
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return geoBuff;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read text result-set row
|
||
|
*
|
||
|
* see: https://mariadb.com/kb/en/library/resultset-row/#text-resultset-row
|
||
|
* data are created according to their type.
|
||
|
*
|
||
|
* @param columns columns metadata
|
||
|
* @param packet current row packet
|
||
|
* @param connOpts connection options
|
||
|
* @returns {*} row data
|
||
|
*/
|
||
|
parseRow(columns, packet, connOpts) {
|
||
|
throw new Error('not implemented');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = CommonBinary;
|