295 lines
8.4 KiB
JavaScript
295 lines
8.4 KiB
JavaScript
'use strict';
|
|
const Long = require('long');
|
|
const hexArray = '0123456789ABCDEF'.split('');
|
|
const Errors = require('../misc/errors');
|
|
const CommonText = require('../cmd/common-text-cmd');
|
|
const Iconv = require('iconv-lite');
|
|
|
|
/**
|
|
* Write bytes/hexadecimal value of a byte array to a string.
|
|
* String output example :
|
|
* 38 00 00 00 03 63 72 65 61 74 65 20 74 61 62 6C 8....create tabl
|
|
* 65 20 42 6C 6F 62 54 65 73 74 63 6C 6F 62 74 65 e BlobTestclobte
|
|
* 73 74 32 20 28 73 74 72 6D 20 74 65 78 74 29 20 st2 (strm text)
|
|
* 43 48 41 52 53 45 54 20 75 74 66 38 CHARSET utf8
|
|
*/
|
|
module.exports.log = function (opts, buf, off, end, header) {
|
|
let out = [];
|
|
if (!buf) return '';
|
|
if (off === undefined || off === null) off = 0;
|
|
if (end === undefined || end === null) end = buf.length;
|
|
let asciiValue = new Array(16);
|
|
asciiValue[8] = ' ';
|
|
|
|
let useHeader = header !== undefined;
|
|
let offset = off || 0;
|
|
const maxLgh = Math.min(useHeader ? opts.debugLen - header.length : opts.debugLen, end - offset);
|
|
const isLimited = end - offset > maxLgh;
|
|
let byteValue;
|
|
let posHexa = 0;
|
|
let pos = 0;
|
|
|
|
out.push(
|
|
'+--------------------------------------------------+\n' +
|
|
'| 0 1 2 3 4 5 6 7 8 9 a b c d e f |\n' +
|
|
'+--------------------------------------------------+------------------+\n'
|
|
);
|
|
|
|
if (useHeader) {
|
|
while (pos < header.length) {
|
|
if (posHexa === 0) out.push('| ');
|
|
byteValue = header[pos++] & 0xff;
|
|
out.push(hexArray[byteValue >>> 4], hexArray[byteValue & 0x0f], ' ');
|
|
asciiValue[posHexa++] =
|
|
byteValue > 31 && byteValue < 127 ? String.fromCharCode(byteValue) : '.';
|
|
if (posHexa === 8) out.push(' ');
|
|
}
|
|
}
|
|
|
|
pos = offset;
|
|
while (pos < maxLgh + offset) {
|
|
if (posHexa === 0) out.push('| ');
|
|
byteValue = buf[pos] & 0xff;
|
|
|
|
out.push(hexArray[byteValue >>> 4], hexArray[byteValue & 0x0f], ' ');
|
|
|
|
asciiValue[posHexa++] =
|
|
byteValue > 31 && byteValue < 127 ? String.fromCharCode(byteValue) : '.';
|
|
|
|
if (posHexa === 8) out.push(' ');
|
|
if (posHexa === 16) {
|
|
out.push('| ', asciiValue.join(''), ' |\n');
|
|
posHexa = 0;
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
let remaining = posHexa;
|
|
if (remaining > 0) {
|
|
if (remaining < 8) {
|
|
for (; remaining < 8; remaining++) {
|
|
out.push(' ');
|
|
asciiValue[posHexa++] = ' ';
|
|
}
|
|
out.push(' ');
|
|
}
|
|
|
|
for (; remaining < 16; remaining++) {
|
|
out.push(' ');
|
|
asciiValue[posHexa++] = ' ';
|
|
}
|
|
|
|
out.push('| ', asciiValue.join(''), isLimited ? ' |...\n' : ' |\n');
|
|
} else if (isLimited) {
|
|
out[out.length - 1] = ' |...\n';
|
|
}
|
|
out.push('+--------------------------------------------------+------------------+\n');
|
|
return out.join('');
|
|
};
|
|
|
|
module.exports.escapeId = (opts, info, value) => {
|
|
if (!value || value === '') {
|
|
throw Errors.createError(
|
|
'Cannot escape empty ID value',
|
|
false,
|
|
info,
|
|
'0A000',
|
|
Errors.ER_NULL_ESCAPEID
|
|
);
|
|
}
|
|
if (value.includes('\u0000')) {
|
|
throw Errors.createError(
|
|
'Cannot escape ID with null character (u0000)',
|
|
false,
|
|
info,
|
|
'0A000',
|
|
Errors.ER_NULL_CHAR_ESCAPEID
|
|
);
|
|
}
|
|
|
|
// always return escaped value, event when there is no special characters
|
|
// to permit working with reserved words
|
|
if (value.match(/^`.+`$/g)) {
|
|
// already escaped
|
|
return value;
|
|
}
|
|
return '`' + value.replace(/`/g, '``') + '`';
|
|
};
|
|
|
|
module.exports.escape = (opts, info, value) => {
|
|
if (value === undefined || value === null) return 'NULL';
|
|
|
|
switch (typeof value) {
|
|
case 'boolean':
|
|
return value ? 'true' : 'false';
|
|
case 'bigint':
|
|
case 'number':
|
|
return '' + value;
|
|
case 'object':
|
|
if (Object.prototype.toString.call(value) === '[object Date]') {
|
|
return opts.tz
|
|
? opts.tz === 'Etc/UTC'
|
|
? CommonText.getUtcDate(value, opts)
|
|
: CommonText.getTimezoneDate(value, opts)
|
|
: CommonText.getLocalDate(value, opts);
|
|
} else if (Buffer.isBuffer(value)) {
|
|
let stValue;
|
|
if (Buffer.isEncoding(opts.collation.charset)) {
|
|
stValue = value.toString(opts.collation.charset, 0, value.length);
|
|
} else {
|
|
stValue = Iconv.decode(value, opts.collation.charset);
|
|
}
|
|
return "_binary'" + escapeString(stValue) + "'";
|
|
} else if (typeof value.toSqlString === 'function') {
|
|
return "'" + escapeString(String(value.toSqlString())) + "'";
|
|
} else if (Long.isLong(value)) {
|
|
return value.toString();
|
|
} else if (Array.isArray(value)) {
|
|
let out = opts.arrayParenthesis ? '(' : '';
|
|
for (let i = 0; i < value.length; i++) {
|
|
if (i !== 0) out += ',';
|
|
out += this.escape(opts, info, value[i]);
|
|
}
|
|
if (opts.arrayParenthesis) out += ')';
|
|
return out;
|
|
} else {
|
|
if (
|
|
value.type != null &&
|
|
[
|
|
'Point',
|
|
'LineString',
|
|
'Polygon',
|
|
'MultiPoint',
|
|
'MultiLineString',
|
|
'MultiPolygon',
|
|
'GeometryCollection'
|
|
].includes(value.type)
|
|
) {
|
|
//GeoJSON format.
|
|
let prefix =
|
|
info &&
|
|
((info.isMariaDB() && info.hasMinVersion(10, 1, 4)) ||
|
|
(!info.isMariaDB() && info.hasMinVersion(5, 7, 6)))
|
|
? 'ST_'
|
|
: '';
|
|
switch (value.type) {
|
|
case 'Point':
|
|
return (
|
|
prefix +
|
|
"PointFromText('POINT(" +
|
|
CommonText.geoPointToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
|
|
case 'LineString':
|
|
return (
|
|
prefix +
|
|
"LineFromText('LINESTRING(" +
|
|
CommonText.geoArrayPointToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
|
|
case 'Polygon':
|
|
return (
|
|
prefix +
|
|
"PolygonFromText('POLYGON(" +
|
|
CommonText.geoMultiArrayPointToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
|
|
case 'MultiPoint':
|
|
return (
|
|
prefix +
|
|
"MULTIPOINTFROMTEXT('MULTIPOINT(" +
|
|
CommonText.geoArrayPointToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
|
|
case 'MultiLineString':
|
|
return (
|
|
prefix +
|
|
"MLineFromText('MULTILINESTRING(" +
|
|
CommonText.geoMultiArrayPointToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
|
|
case 'MultiPolygon':
|
|
return (
|
|
prefix +
|
|
"MPolyFromText('MULTIPOLYGON(" +
|
|
CommonText.geoMultiPolygonToString(value.coordinates) +
|
|
")')"
|
|
);
|
|
|
|
case 'GeometryCollection':
|
|
return (
|
|
prefix +
|
|
"GeomCollFromText('GEOMETRYCOLLECTION(" +
|
|
CommonText.geometricCollectionToString(value.geometries) +
|
|
")')"
|
|
);
|
|
}
|
|
} else {
|
|
if (opts.permitSetMultiParamEntries) {
|
|
let out = '';
|
|
let first = true;
|
|
for (let key in value) {
|
|
const val = value[key];
|
|
if (typeof val === 'function') continue;
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
out += ',';
|
|
}
|
|
out += '`' + key + '`=';
|
|
out += this.escape(opts, info, val);
|
|
}
|
|
if (out === '') return "'" + escapeString(JSON.stringify(value)) + "'";
|
|
return out;
|
|
} else {
|
|
return "'" + escapeString(JSON.stringify(value)) + "'";
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
return "'" + escapeString(value) + "'";
|
|
}
|
|
};
|
|
|
|
// see https://mariadb.com/kb/en/library/string-literals/
|
|
const LITTERAL_ESCAPE = {
|
|
'\u0000': '\\0',
|
|
"'": "\\'",
|
|
'"': '\\"',
|
|
'\b': '\\b',
|
|
'\n': '\\n',
|
|
'\r': '\\r',
|
|
'\t': '\\t',
|
|
'\u001A': '\\Z',
|
|
'\\': '\\\\'
|
|
};
|
|
|
|
const escapeString = (val) => {
|
|
const pattern = /[\u0000'"\b\n\r\t\u001A\\]/g;
|
|
|
|
let offset = 0;
|
|
let escaped = '';
|
|
let match;
|
|
|
|
while ((match = pattern.exec(val))) {
|
|
escaped += val.substring(offset, match.index);
|
|
escaped += LITTERAL_ESCAPE[match[0]];
|
|
offset = pattern.lastIndex;
|
|
}
|
|
|
|
if (offset === 0) {
|
|
return val;
|
|
}
|
|
|
|
if (offset < val.length) {
|
|
escaped += val.substring(offset);
|
|
}
|
|
|
|
return escaped;
|
|
};
|