288 lines
8.5 KiB
JavaScript
Executable File
288 lines
8.5 KiB
JavaScript
Executable File
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
// Copyright (c) 2015-2024 MariaDB Corporation Ab
|
|
|
|
'use strict';
|
|
|
|
const QUOTE = 0x27;
|
|
|
|
const formatDigit = function (val, significantDigit) {
|
|
let res = `${val}`;
|
|
while (res.length < significantDigit) res = '0' + res;
|
|
return res;
|
|
};
|
|
|
|
class TextEncoder {
|
|
/**
|
|
* Write (and escape) current parameter value to output writer
|
|
*
|
|
* @param out output writer
|
|
* @param value current parameter. Expected to be non-null
|
|
* @param opts connection options
|
|
* @param info connection information
|
|
*/
|
|
static writeParam(out, value, opts, info) {
|
|
switch (typeof value) {
|
|
case 'boolean':
|
|
out.writeStringAscii(value ? 'true' : 'false');
|
|
break;
|
|
case 'bigint':
|
|
case 'number':
|
|
out.writeStringAscii(`${value}`);
|
|
break;
|
|
case 'string':
|
|
out.writeStringEscapeQuote(value);
|
|
break;
|
|
case 'object':
|
|
if (Object.prototype.toString.call(value) === '[object Date]') {
|
|
out.writeStringAscii(TextEncoder.getLocalDate(value));
|
|
} else if (Buffer.isBuffer(value)) {
|
|
out.writeStringAscii("_BINARY '");
|
|
out.writeBufferEscape(value);
|
|
out.writeInt8(QUOTE);
|
|
} else if (typeof value.toSqlString === 'function') {
|
|
out.writeStringEscapeQuote(String(value.toSqlString()));
|
|
} else if (Array.isArray(value)) {
|
|
if (opts.arrayParenthesis) {
|
|
out.writeStringAscii('(');
|
|
}
|
|
for (let i = 0; i < value.length; i++) {
|
|
if (i !== 0) out.writeStringAscii(',');
|
|
if (value[i] == null) {
|
|
out.writeStringAscii('NULL');
|
|
} else TextEncoder.writeParam(out, value[i], opts, info);
|
|
}
|
|
if (opts.arrayParenthesis) {
|
|
out.writeStringAscii(')');
|
|
}
|
|
} else {
|
|
if (
|
|
value.type != null &&
|
|
[
|
|
'Point',
|
|
'LineString',
|
|
'Polygon',
|
|
'MultiPoint',
|
|
'MultiLineString',
|
|
'MultiPolygon',
|
|
'GeometryCollection'
|
|
].includes(value.type)
|
|
) {
|
|
//GeoJSON format.
|
|
let prefix =
|
|
(info.isMariaDB() && info.hasMinVersion(10, 1, 4)) || (!info.isMariaDB() && info.hasMinVersion(5, 7, 6))
|
|
? 'ST_'
|
|
: '';
|
|
switch (value.type) {
|
|
case 'Point':
|
|
out.writeStringAscii(
|
|
prefix + "PointFromText('POINT(" + TextEncoder.geoPointToString(value.coordinates) + ")')"
|
|
);
|
|
break;
|
|
|
|
case 'LineString':
|
|
out.writeStringAscii(
|
|
prefix + "LineFromText('LINESTRING(" + TextEncoder.geoArrayPointToString(value.coordinates) + ")')"
|
|
);
|
|
break;
|
|
|
|
case 'Polygon':
|
|
out.writeStringAscii(
|
|
prefix +
|
|
"PolygonFromText('POLYGON(" +
|
|
TextEncoder.geoMultiArrayPointToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
break;
|
|
|
|
case 'MultiPoint':
|
|
out.writeStringAscii(
|
|
prefix +
|
|
"MULTIPOINTFROMTEXT('MULTIPOINT(" +
|
|
TextEncoder.geoArrayPointToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
break;
|
|
|
|
case 'MultiLineString':
|
|
out.writeStringAscii(
|
|
prefix +
|
|
"MLineFromText('MULTILINESTRING(" +
|
|
TextEncoder.geoMultiArrayPointToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
break;
|
|
|
|
case 'MultiPolygon':
|
|
out.writeStringAscii(
|
|
prefix +
|
|
"MPolyFromText('MULTIPOLYGON(" +
|
|
TextEncoder.geoMultiPolygonToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
break;
|
|
|
|
case 'GeometryCollection':
|
|
out.writeStringAscii(
|
|
prefix +
|
|
"GeomCollFromText('GEOMETRYCOLLECTION(" +
|
|
TextEncoder.geometricCollectionToString(value.geometries) +
|
|
")')"
|
|
);
|
|
break;
|
|
}
|
|
} else if (String === value.constructor) {
|
|
out.writeStringEscapeQuote(value);
|
|
break;
|
|
} else {
|
|
if (opts.permitSetMultiParamEntries) {
|
|
let first = true;
|
|
for (let key in value) {
|
|
const val = value[key];
|
|
if (typeof val === 'function') continue;
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
out.writeStringAscii(',');
|
|
}
|
|
out.writeString('`' + key + '`');
|
|
if (val == null) {
|
|
out.writeStringAscii('=NULL');
|
|
} else {
|
|
out.writeStringAscii('=');
|
|
TextEncoder.writeParam(out, val, opts, info);
|
|
}
|
|
}
|
|
if (first) out.writeStringEscapeQuote(JSON.stringify(value));
|
|
} else {
|
|
out.writeStringEscapeQuote(JSON.stringify(value));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static geometricCollectionToString(geo) {
|
|
if (!geo) return '';
|
|
let st = '';
|
|
for (let i = 0; i < geo.length; i++) {
|
|
//GeoJSON format.
|
|
st += i !== 0 ? ',' : '';
|
|
switch (geo[i].type) {
|
|
case 'Point':
|
|
st += `POINT(${TextEncoder.geoPointToString(geo[i].coordinates)})`;
|
|
break;
|
|
|
|
case 'LineString':
|
|
st += `LINESTRING(${TextEncoder.geoArrayPointToString(geo[i].coordinates)})`;
|
|
break;
|
|
|
|
case 'Polygon':
|
|
st += `POLYGON(${TextEncoder.geoMultiArrayPointToString(geo[i].coordinates)})`;
|
|
break;
|
|
|
|
case 'MultiPoint':
|
|
st += `MULTIPOINT(${TextEncoder.geoArrayPointToString(geo[i].coordinates)})`;
|
|
break;
|
|
|
|
case 'MultiLineString':
|
|
st += `MULTILINESTRING(${TextEncoder.geoMultiArrayPointToString(geo[i].coordinates)})`;
|
|
break;
|
|
|
|
case 'MultiPolygon':
|
|
st += `MULTIPOLYGON(${TextEncoder.geoMultiPolygonToString(geo[i].coordinates)})`;
|
|
break;
|
|
}
|
|
}
|
|
return st;
|
|
}
|
|
|
|
static geoMultiPolygonToString(coords) {
|
|
if (!coords) return '';
|
|
let st = '';
|
|
for (let i = 0; i < coords.length; i++) {
|
|
st += (i !== 0 ? ',(' : '(') + TextEncoder.geoMultiArrayPointToString(coords[i]) + ')';
|
|
}
|
|
return st;
|
|
}
|
|
|
|
static geoMultiArrayPointToString(coords) {
|
|
if (!coords) return '';
|
|
let st = '';
|
|
for (let i = 0; i < coords.length; i++) {
|
|
st += (i !== 0 ? ',(' : '(') + TextEncoder.geoArrayPointToString(coords[i]) + ')';
|
|
}
|
|
return st;
|
|
}
|
|
|
|
static geoArrayPointToString(coords) {
|
|
if (!coords) return '';
|
|
let st = '';
|
|
for (let i = 0; i < coords.length; i++) {
|
|
st += (i !== 0 ? ',' : '') + TextEncoder.geoPointToString(coords[i]);
|
|
}
|
|
return st;
|
|
}
|
|
|
|
static geoPointToString(coords) {
|
|
if (!coords) return '';
|
|
return (isNaN(coords[0]) ? '' : coords[0]) + ' ' + (isNaN(coords[1]) ? '' : coords[1]);
|
|
}
|
|
|
|
static getLocalDate(date) {
|
|
const ms = date.getMilliseconds();
|
|
|
|
//return 'YYYY-MM-DD HH:MM:SS' datetime format
|
|
//see https://mariadb.com/kb/en/library/datetime/
|
|
let d =
|
|
"'" +
|
|
date.getFullYear() +
|
|
'-' +
|
|
(date.getMonth() + 1) +
|
|
'-' +
|
|
date.getDate() +
|
|
' ' +
|
|
date.getHours() +
|
|
':' +
|
|
date.getMinutes() +
|
|
':' +
|
|
date.getSeconds();
|
|
if (ms === 0) return d + "'";
|
|
|
|
let res = `${ms}`;
|
|
while (res.length < 3) res = '0' + res;
|
|
return d + '.' + res + "'";
|
|
}
|
|
|
|
static getFixedFormatDate(date) {
|
|
const year = date.getFullYear();
|
|
const mon = date.getMonth() + 1;
|
|
const day = date.getDate();
|
|
const hour = date.getHours();
|
|
const min = date.getMinutes();
|
|
const sec = date.getSeconds();
|
|
const ms = date.getMilliseconds();
|
|
|
|
//return 'YYYY-MM-DD HH:MM:SS' datetime format
|
|
//see https://mariadb.com/kb/en/library/datetime/
|
|
return (
|
|
"'" +
|
|
formatDigit(year, 4) +
|
|
'-' +
|
|
formatDigit(mon, 2) +
|
|
'-' +
|
|
formatDigit(day, 2) +
|
|
' ' +
|
|
formatDigit(hour, 2) +
|
|
':' +
|
|
formatDigit(min, 2) +
|
|
':' +
|
|
formatDigit(sec, 2) +
|
|
(ms > 0 ? '.' + formatDigit(ms, 3) : '') +
|
|
"'"
|
|
);
|
|
}
|
|
}
|
|
|
|
module.exports = TextEncoder;
|