bigint support #1087

This commit is contained in:
SPRINX0\prochazka
2025-05-05 16:04:21 +02:00
parent 23db345756
commit 110d87e512
11 changed files with 93 additions and 15 deletions

View File

@@ -12,6 +12,7 @@ const {
ScriptWriterEval,
SqlGenerator,
playJsonScriptWriter,
serializeJsTypesForJsonStringify,
} = require('dbgate-tools');
const requireEngineDriver = require('../utility/requireEngineDriver');
const { connectUtility } = require('../utility/connectUtility');
@@ -232,7 +233,7 @@ async function handleQueryData({ msgid, sql, range }, skipReadonlyCheck = false)
try {
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
const res = await driver.query(dbhan, sql, { range });
process.send({ msgtype: 'response', msgid, ...res });
process.send({ msgtype: 'response', msgid, ...serializeJsTypesForJsonStringify(res) });
} catch (err) {
process.send({
msgtype: 'response',

View File

@@ -4,6 +4,7 @@ const fs = require('fs');
const _ = require('lodash');
const { jsldir } = require('../utility/directories');
const { serializeJsTypesReplacer } = require('dbgate-tools');
class QueryStreamTableWriter {
constructor(sesid = undefined) {
@@ -38,7 +39,7 @@ class QueryStreamTableWriter {
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;
if (!this.plannedStats) {

View File

@@ -20,6 +20,7 @@ export function getFilterValueExpression(value, dataType?) {
if (value === true) return 'TRUE';
if (value === false) return 'FALSE';
if (value.$oid) return `ObjectId("${value.$oid}")`;
if (value.$bigint) return value.$bigint;
if (value.type == 'Buffer' && Array.isArray(value.data)) {
return '0x' + arrayToHexString(value.data);
}

View File

@@ -2,14 +2,18 @@ import P from 'parsimmon';
import moment from 'moment';
import { Condition } from 'dbgate-sqltree';
import { interpretEscapes, token, word, whitespace } from './common';
import { hexStringToArray } from 'dbgate-tools';
import { hexStringToArray, parseNumberSafe } from 'dbgate-tools';
import { FilterBehaviour, TransformType } from 'dbgate-types';
const binaryCondition =
(operator, numberDualTesting = false) =>
value => {
const numValue = parseFloat(value);
if (numberDualTesting && !isNaN(numValue)) {
const numValue = parseNumberSafe(value);
if (
numberDualTesting &&
// @ts-ignore
!isNaN(numValue)
) {
return {
conditionType: 'or',
conditions: [
@@ -345,17 +349,17 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
string1Num: () =>
token(P.regexp(/"-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?"/, 1))
.map(Number)
.map(parseNumberSafe)
.desc('numer quoted'),
string2Num: () =>
token(P.regexp(/'-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?'/, 1))
.map(Number)
.map(parseNumberSafe)
.desc('numer quoted'),
number: () =>
token(P.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/))
.map(Number)
.map(parseNumberSafe)
.desc('number'),
objectid: () => token(P.regexp(/ObjectId\(['"]?[0-9a-f]{24}['"]?\)/)).desc('ObjectId'),

View File

@@ -16,11 +16,17 @@ function isLike(value, test) {
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) {
switch (condition.conditionType) {
case 'binary':
const left = evaluateExpression(condition.left, values);
const right = evaluateExpression(condition.right, values);
const left = extractRawValue(evaluateExpression(condition.left, values));
const right = extractRawValue(evaluateExpression(condition.right, values));
switch (condition.operator) {
case '=':
return left == right;
@@ -50,10 +56,15 @@ export function evaluateCondition(condition: Condition, values) {
case 'or':
return condition.conditions.some(cond => evaluateCondition(cond, values));
case 'like':
return isLike(evaluateExpression(condition.left, values), evaluateExpression(condition.right, values));
break;
return isLike(
extractRawValue(evaluateExpression(condition.left, values)),
extractRawValue(evaluateExpression(condition.right, values))
);
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':
return !evaluateCondition(condition.condition, values);
case 'anyColumnPass':

View File

@@ -78,6 +78,7 @@ export class SqlDumper implements AlterProcessor {
else if (_isNumber(value)) this.putRaw(value.toString());
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?.$bigint) this.putRaw(value?.$bigint);
else if (_isPlainObject(value) || _isArray(value)) this.putStringValue(JSON.stringify(value));
else this.put('^null');
}

View File

@@ -4,6 +4,7 @@ import _isDate from 'lodash/isDate';
import _isNumber from 'lodash/isNumber';
import _isPlainObject from 'lodash/isPlainObject';
import _pad from 'lodash/pad';
import _cloneDeepWith from 'lodash/cloneDeepWith';
import { DataEditorTypesBehaviour } from 'dbgate-types';
export type EditorDataType =
@@ -208,6 +209,12 @@ export function stringifyCellValue(
}
}
}
if (value?.$bigint) {
return {
value: value.$bigint,
gridStyle: 'valueCellStyle',
};
}
if (editorTypes?.parseDateAsDollar) {
if (value?.$date) {
@@ -343,6 +350,9 @@ export function shouldOpenMultilineDialog(value) {
if (value?.$date) {
return false;
}
if (value?.$bigint) {
return false;
}
if (_isPlainObject(value) || _isArray(value)) {
return true;
}
@@ -573,3 +583,44 @@ export function jsonLinesParse(jsonLines: string): any[] {
})
.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);
}

View File

@@ -54,7 +54,8 @@
$: 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
$: jsonParsedValue = !editorTypes?.explicitDataType && isJsonLikeLongString(value) ? safeJsonParse(value) : null;

View File

@@ -72,6 +72,7 @@ export function countColumnSizes(grider: Grider, columns, containerWidth, displa
let text = value;
if (_.isArray(value)) text = `[${value.length} items]`;
else if (value?.$oid) text = `ObjectId("${value.$oid}")`;
else if (value?.$bigint) text = value.$bigint;
else if (isJsonLikeLongString(value) && safeJsonParse(value)) text = '(JSON)';
const width = context.measureText(text).width + 8;
// console.log('colName', colName, text, width);

View File

@@ -13,6 +13,7 @@ import { callServerPing } from './connectionsPinger';
import { batchDispatchCacheTriggers, dispatchCacheChange } from './cache';
import { isAdminPage, isOneOfPage } from './pageDefs';
import { openWebLink } from './simpleTools';
import { serializeJsTypesReplacer } from 'dbgate-tools';
export const strmid = uuidv1();
@@ -177,7 +178,7 @@ export async function apiCall(
'Content-Type': 'application/json',
...resolveApiHeaders(),
},
body: JSON.stringify(args),
body: JSON.stringify(args, serializeJsTypesReplacer),
});
if (resp.status == 401 && !apiDisabled) {

View File

@@ -21,6 +21,11 @@ const logger = getLogger('postreDriver');
pg.types.setTypeParser(1082, 'text', val => val); // date
pg.types.setTypeParser(1114, 'text', val => val); // timestamp without timezone
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) {
try {