mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-17 23:45:59 +00:00
bigint support #1087
This commit is contained in:
@@ -12,6 +12,7 @@ const {
|
|||||||
ScriptWriterEval,
|
ScriptWriterEval,
|
||||||
SqlGenerator,
|
SqlGenerator,
|
||||||
playJsonScriptWriter,
|
playJsonScriptWriter,
|
||||||
|
serializeJsTypesForJsonStringify,
|
||||||
} = require('dbgate-tools');
|
} = require('dbgate-tools');
|
||||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||||
const { connectUtility } = require('../utility/connectUtility');
|
const { connectUtility } = require('../utility/connectUtility');
|
||||||
@@ -232,7 +233,7 @@ async function handleQueryData({ msgid, sql, range }, skipReadonlyCheck = false)
|
|||||||
try {
|
try {
|
||||||
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
|
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
|
||||||
const res = await driver.query(dbhan, sql, { range });
|
const res = await driver.query(dbhan, sql, { range });
|
||||||
process.send({ msgtype: 'response', msgid, ...res });
|
process.send({ msgtype: 'response', msgid, ...serializeJsTypesForJsonStringify(res) });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
process.send({
|
process.send({
|
||||||
msgtype: 'response',
|
msgtype: 'response',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
const { jsldir } = require('../utility/directories');
|
const { jsldir } = require('../utility/directories');
|
||||||
|
const { serializeJsTypesReplacer } = require('dbgate-tools');
|
||||||
|
|
||||||
class QueryStreamTableWriter {
|
class QueryStreamTableWriter {
|
||||||
constructor(sesid = undefined) {
|
constructor(sesid = undefined) {
|
||||||
@@ -38,7 +39,7 @@ class QueryStreamTableWriter {
|
|||||||
|
|
||||||
row(row) {
|
row(row) {
|
||||||
// console.log('ACCEPT ROW', row);
|
// console.log('ACCEPT ROW', row);
|
||||||
this.currentStream.write(JSON.stringify(row) + '\n');
|
this.currentStream.write(JSON.stringify(row, serializeJsTypesReplacer) + '\n');
|
||||||
this.currentRowCount += 1;
|
this.currentRowCount += 1;
|
||||||
|
|
||||||
if (!this.plannedStats) {
|
if (!this.plannedStats) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export function getFilterValueExpression(value, dataType?) {
|
|||||||
if (value === true) return 'TRUE';
|
if (value === true) return 'TRUE';
|
||||||
if (value === false) return 'FALSE';
|
if (value === false) return 'FALSE';
|
||||||
if (value.$oid) return `ObjectId("${value.$oid}")`;
|
if (value.$oid) return `ObjectId("${value.$oid}")`;
|
||||||
|
if (value.$bigint) return value.$bigint;
|
||||||
if (value.type == 'Buffer' && Array.isArray(value.data)) {
|
if (value.type == 'Buffer' && Array.isArray(value.data)) {
|
||||||
return '0x' + arrayToHexString(value.data);
|
return '0x' + arrayToHexString(value.data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,18 @@ import P from 'parsimmon';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Condition } from 'dbgate-sqltree';
|
import { Condition } from 'dbgate-sqltree';
|
||||||
import { interpretEscapes, token, word, whitespace } from './common';
|
import { interpretEscapes, token, word, whitespace } from './common';
|
||||||
import { hexStringToArray } from 'dbgate-tools';
|
import { hexStringToArray, parseNumberSafe } from 'dbgate-tools';
|
||||||
import { FilterBehaviour, TransformType } from 'dbgate-types';
|
import { FilterBehaviour, TransformType } from 'dbgate-types';
|
||||||
|
|
||||||
const binaryCondition =
|
const binaryCondition =
|
||||||
(operator, numberDualTesting = false) =>
|
(operator, numberDualTesting = false) =>
|
||||||
value => {
|
value => {
|
||||||
const numValue = parseFloat(value);
|
const numValue = parseNumberSafe(value);
|
||||||
if (numberDualTesting && !isNaN(numValue)) {
|
if (
|
||||||
|
numberDualTesting &&
|
||||||
|
// @ts-ignore
|
||||||
|
!isNaN(numValue)
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
conditionType: 'or',
|
conditionType: 'or',
|
||||||
conditions: [
|
conditions: [
|
||||||
@@ -345,17 +349,17 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
|
|||||||
|
|
||||||
string1Num: () =>
|
string1Num: () =>
|
||||||
token(P.regexp(/"-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?"/, 1))
|
token(P.regexp(/"-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?"/, 1))
|
||||||
.map(Number)
|
.map(parseNumberSafe)
|
||||||
.desc('numer quoted'),
|
.desc('numer quoted'),
|
||||||
|
|
||||||
string2Num: () =>
|
string2Num: () =>
|
||||||
token(P.regexp(/'-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?'/, 1))
|
token(P.regexp(/'-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?'/, 1))
|
||||||
.map(Number)
|
.map(parseNumberSafe)
|
||||||
.desc('numer quoted'),
|
.desc('numer quoted'),
|
||||||
|
|
||||||
number: () =>
|
number: () =>
|
||||||
token(P.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/))
|
token(P.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/))
|
||||||
.map(Number)
|
.map(parseNumberSafe)
|
||||||
.desc('number'),
|
.desc('number'),
|
||||||
|
|
||||||
objectid: () => token(P.regexp(/ObjectId\(['"]?[0-9a-f]{24}['"]?\)/)).desc('ObjectId'),
|
objectid: () => token(P.regexp(/ObjectId\(['"]?[0-9a-f]{24}['"]?\)/)).desc('ObjectId'),
|
||||||
|
|||||||
@@ -16,11 +16,17 @@ function isLike(value, test) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractRawValue(value) {
|
||||||
|
if (value?.$bigint) return value.$bigint;
|
||||||
|
if (value?.$oid) return value.$oid;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
export function evaluateCondition(condition: Condition, values) {
|
export function evaluateCondition(condition: Condition, values) {
|
||||||
switch (condition.conditionType) {
|
switch (condition.conditionType) {
|
||||||
case 'binary':
|
case 'binary':
|
||||||
const left = evaluateExpression(condition.left, values);
|
const left = extractRawValue(evaluateExpression(condition.left, values));
|
||||||
const right = evaluateExpression(condition.right, values);
|
const right = extractRawValue(evaluateExpression(condition.right, values));
|
||||||
switch (condition.operator) {
|
switch (condition.operator) {
|
||||||
case '=':
|
case '=':
|
||||||
return left == right;
|
return left == right;
|
||||||
@@ -50,10 +56,15 @@ export function evaluateCondition(condition: Condition, values) {
|
|||||||
case 'or':
|
case 'or':
|
||||||
return condition.conditions.some(cond => evaluateCondition(cond, values));
|
return condition.conditions.some(cond => evaluateCondition(cond, values));
|
||||||
case 'like':
|
case 'like':
|
||||||
return isLike(evaluateExpression(condition.left, values), evaluateExpression(condition.right, values));
|
return isLike(
|
||||||
break;
|
extractRawValue(evaluateExpression(condition.left, values)),
|
||||||
|
extractRawValue(evaluateExpression(condition.right, values))
|
||||||
|
);
|
||||||
case 'notLike':
|
case 'notLike':
|
||||||
return !isLike(evaluateExpression(condition.left, values), evaluateExpression(condition.right, values));
|
return !isLike(
|
||||||
|
extractRawValue(evaluateExpression(condition.left, values)),
|
||||||
|
extractRawValue(evaluateExpression(condition.right, values))
|
||||||
|
);
|
||||||
case 'not':
|
case 'not':
|
||||||
return !evaluateCondition(condition.condition, values);
|
return !evaluateCondition(condition.condition, values);
|
||||||
case 'anyColumnPass':
|
case 'anyColumnPass':
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export class SqlDumper implements AlterProcessor {
|
|||||||
else if (_isNumber(value)) this.putRaw(value.toString());
|
else if (_isNumber(value)) this.putRaw(value.toString());
|
||||||
else if (_isDate(value)) this.putStringValue(new Date(value).toISOString());
|
else if (_isDate(value)) this.putStringValue(new Date(value).toISOString());
|
||||||
else if (value?.type == 'Buffer' && _isArray(value?.data)) this.putByteArrayValue(value?.data);
|
else if (value?.type == 'Buffer' && _isArray(value?.data)) this.putByteArrayValue(value?.data);
|
||||||
|
else if (value?.$bigint) this.putRaw(value?.$bigint);
|
||||||
else if (_isPlainObject(value) || _isArray(value)) this.putStringValue(JSON.stringify(value));
|
else if (_isPlainObject(value) || _isArray(value)) this.putStringValue(JSON.stringify(value));
|
||||||
else this.put('^null');
|
else this.put('^null');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import _isDate from 'lodash/isDate';
|
|||||||
import _isNumber from 'lodash/isNumber';
|
import _isNumber from 'lodash/isNumber';
|
||||||
import _isPlainObject from 'lodash/isPlainObject';
|
import _isPlainObject from 'lodash/isPlainObject';
|
||||||
import _pad from 'lodash/pad';
|
import _pad from 'lodash/pad';
|
||||||
|
import _cloneDeepWith from 'lodash/cloneDeepWith';
|
||||||
import { DataEditorTypesBehaviour } from 'dbgate-types';
|
import { DataEditorTypesBehaviour } from 'dbgate-types';
|
||||||
|
|
||||||
export type EditorDataType =
|
export type EditorDataType =
|
||||||
@@ -208,6 +209,12 @@ export function stringifyCellValue(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (value?.$bigint) {
|
||||||
|
return {
|
||||||
|
value: value.$bigint,
|
||||||
|
gridStyle: 'valueCellStyle',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (editorTypes?.parseDateAsDollar) {
|
if (editorTypes?.parseDateAsDollar) {
|
||||||
if (value?.$date) {
|
if (value?.$date) {
|
||||||
@@ -343,6 +350,9 @@ export function shouldOpenMultilineDialog(value) {
|
|||||||
if (value?.$date) {
|
if (value?.$date) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (value?.$bigint) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (_isPlainObject(value) || _isArray(value)) {
|
if (_isPlainObject(value) || _isArray(value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -573,3 +583,44 @@ export function jsonLinesParse(jsonLines: string): any[] {
|
|||||||
})
|
})
|
||||||
.filter(x => x);
|
.filter(x => x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function serializeJsTypesForJsonStringify(obj) {
|
||||||
|
return _cloneDeepWith(obj, value => {
|
||||||
|
if (typeof value === 'bigint') {
|
||||||
|
return { $bigint: value.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deserializeJsTypesFromJsonParse(obj) {
|
||||||
|
return _cloneDeepWith(obj, value => {
|
||||||
|
if (value?.$bigint) {
|
||||||
|
return BigInt(value.$bigint);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializeJsTypesReplacer(key, value) {
|
||||||
|
if (typeof value === 'bigint') {
|
||||||
|
return { $bigint: value.toString() };
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deserializeJsTypesReviver(key, value) {
|
||||||
|
if (value?.$bigint) {
|
||||||
|
return BigInt(value.$bigint);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseNumberSafe(value) {
|
||||||
|
if (/^-?[0-9]+$/.test(value)) {
|
||||||
|
const parsed = parseInt(value);
|
||||||
|
if (Number.isSafeInteger(parsed)) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
return BigInt(value);
|
||||||
|
}
|
||||||
|
return parseFloat(value);
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
|
|
||||||
$: style = computeStyle(maxWidth, col);
|
$: style = computeStyle(maxWidth, col);
|
||||||
|
|
||||||
$: isJson = _.isPlainObject(value) && !(value?.type == 'Buffer' && _.isArray(value.data)) && !value.$oid;
|
$: isJson =
|
||||||
|
_.isPlainObject(value) && !(value?.type == 'Buffer' && _.isArray(value.data)) && !value.$oid && !value.$bigint;
|
||||||
|
|
||||||
// don't parse JSON for explicit data types
|
// don't parse JSON for explicit data types
|
||||||
$: jsonParsedValue = !editorTypes?.explicitDataType && isJsonLikeLongString(value) ? safeJsonParse(value) : null;
|
$: jsonParsedValue = !editorTypes?.explicitDataType && isJsonLikeLongString(value) ? safeJsonParse(value) : null;
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export function countColumnSizes(grider: Grider, columns, containerWidth, displa
|
|||||||
let text = value;
|
let text = value;
|
||||||
if (_.isArray(value)) text = `[${value.length} items]`;
|
if (_.isArray(value)) text = `[${value.length} items]`;
|
||||||
else if (value?.$oid) text = `ObjectId("${value.$oid}")`;
|
else if (value?.$oid) text = `ObjectId("${value.$oid}")`;
|
||||||
|
else if (value?.$bigint) text = value.$bigint;
|
||||||
else if (isJsonLikeLongString(value) && safeJsonParse(value)) text = '(JSON)';
|
else if (isJsonLikeLongString(value) && safeJsonParse(value)) text = '(JSON)';
|
||||||
const width = context.measureText(text).width + 8;
|
const width = context.measureText(text).width + 8;
|
||||||
// console.log('colName', colName, text, width);
|
// console.log('colName', colName, text, width);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { callServerPing } from './connectionsPinger';
|
|||||||
import { batchDispatchCacheTriggers, dispatchCacheChange } from './cache';
|
import { batchDispatchCacheTriggers, dispatchCacheChange } from './cache';
|
||||||
import { isAdminPage, isOneOfPage } from './pageDefs';
|
import { isAdminPage, isOneOfPage } from './pageDefs';
|
||||||
import { openWebLink } from './simpleTools';
|
import { openWebLink } from './simpleTools';
|
||||||
|
import { serializeJsTypesReplacer } from 'dbgate-tools';
|
||||||
|
|
||||||
export const strmid = uuidv1();
|
export const strmid = uuidv1();
|
||||||
|
|
||||||
@@ -177,7 +178,7 @@ export async function apiCall(
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...resolveApiHeaders(),
|
...resolveApiHeaders(),
|
||||||
},
|
},
|
||||||
body: JSON.stringify(args),
|
body: JSON.stringify(args, serializeJsTypesReplacer),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (resp.status == 401 && !apiDisabled) {
|
if (resp.status == 401 && !apiDisabled) {
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ const logger = getLogger('postreDriver');
|
|||||||
pg.types.setTypeParser(1082, 'text', val => val); // date
|
pg.types.setTypeParser(1082, 'text', val => val); // date
|
||||||
pg.types.setTypeParser(1114, 'text', val => val); // timestamp without timezone
|
pg.types.setTypeParser(1114, 'text', val => val); // timestamp without timezone
|
||||||
pg.types.setTypeParser(1184, 'text', val => val); // timestamp
|
pg.types.setTypeParser(1184, 'text', val => val); // timestamp
|
||||||
|
pg.types.setTypeParser(20, 'text', val => {
|
||||||
|
const parsed = parseInt(val);
|
||||||
|
if (Number.isSafeInteger(parsed)) return parsed;
|
||||||
|
return BigInt(val);
|
||||||
|
}); // timestamp
|
||||||
|
|
||||||
function extractGeographyDate(value) {
|
function extractGeographyDate(value) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user