fix: 修复mysql中dateDiff、dateCeil、dateFloor的部分问题
This commit is contained in:
parent
f6322dffff
commit
3a17335937
|
|
@ -796,10 +796,11 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
|
||||||
case '$dayOfYear': {
|
case '$dayOfYear': {
|
||||||
return 'DAYOFYEAR(%s)';
|
return 'DAYOFYEAR(%s)';
|
||||||
}
|
}
|
||||||
case '$dateDiff': {
|
// 这个实现有问题,DATEDIFF只是计算两个日期之间的天数差,只接受两个参数,放在translateExperession里实现
|
||||||
(0, assert_1.default)(argumentNumber === 3);
|
// case '$dateDiff': {
|
||||||
return 'DATEDIFF(%s, %s, %s)';
|
// assert(argumentNumber === 3);
|
||||||
}
|
// return 'DATEDIFF(%s, %s, %s)';
|
||||||
|
// }
|
||||||
case '$contains': {
|
case '$contains': {
|
||||||
(0, assert_1.default)(argumentNumber === 2);
|
(0, assert_1.default)(argumentNumber === 2);
|
||||||
return 'ST_CONTAINS(%s, %s)';
|
return 'ST_CONTAINS(%s, %s)';
|
||||||
|
|
@ -816,6 +817,22 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
|
||||||
result += ')';
|
result += ')';
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
// ========== 聚合函数 ==========
|
||||||
|
case '$$count': {
|
||||||
|
return 'COUNT(%s)';
|
||||||
|
}
|
||||||
|
case '$$sum': {
|
||||||
|
return 'SUM(%s)';
|
||||||
|
}
|
||||||
|
case '$$max': {
|
||||||
|
return 'MAX(%s)';
|
||||||
|
}
|
||||||
|
case '$$min': {
|
||||||
|
return 'MIN(%s)';
|
||||||
|
}
|
||||||
|
case '$$avg': {
|
||||||
|
return 'AVG(%s)';
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new Error(`unrecoganized function ${fnName}`);
|
throw new Error(`unrecoganized function ${fnName}`);
|
||||||
}
|
}
|
||||||
|
|
@ -859,10 +876,135 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
(0, assert_1.default)(k.length === 1);
|
(0, assert_1.default)(k.length === 1);
|
||||||
if ((expr)[k[0]] instanceof Array) {
|
const fnKey = k[0];
|
||||||
const fnName = this.translateFnName(k[0], (expr)[k[0]].length);
|
const fnArgs = (expr)[fnKey];
|
||||||
|
// 特殊处理日期相关函数
|
||||||
|
if (fnKey === '$dateDiff') {
|
||||||
|
// $dateDiff: [date1, date2, unit]
|
||||||
|
(0, assert_1.default)(fnArgs instanceof Array && fnArgs.length === 3);
|
||||||
|
const [date1Expr, date2Expr, unit] = fnArgs;
|
||||||
|
// 转换日期表达式
|
||||||
|
const translateDateArg = (arg) => {
|
||||||
|
if (arg instanceof Date) {
|
||||||
|
return `FROM_UNIXTIME(${arg.valueOf()} / 1000)`;
|
||||||
|
}
|
||||||
|
else if (typeof arg === 'number') {
|
||||||
|
return `FROM_UNIXTIME(${arg} / 1000)`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return translateInner(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const date1Str = translateDateArg(date1Expr);
|
||||||
|
const date2Str = translateDateArg(date2Expr);
|
||||||
|
// MySQL TIMESTAMPDIFF 单位映射
|
||||||
|
const unitMap = {
|
||||||
|
's': 'SECOND',
|
||||||
|
'm': 'MINUTE',
|
||||||
|
'h': 'HOUR',
|
||||||
|
'd': 'DAY',
|
||||||
|
'M': 'MONTH',
|
||||||
|
'y': 'YEAR'
|
||||||
|
};
|
||||||
|
const mysqlUnit = unitMap[unit];
|
||||||
|
if (!mysqlUnit) {
|
||||||
|
throw new Error(`Unsupported date diff unit: ${unit}`);
|
||||||
|
}
|
||||||
|
// TIMESTAMPDIFF(unit, date1, date2) 返回 date2 - date1
|
||||||
|
// 但类型定义是 date1 - date2,所以参数顺序要反过来
|
||||||
|
result = `TIMESTAMPDIFF(${mysqlUnit}, ${date2Str}, ${date1Str})`;
|
||||||
|
}
|
||||||
|
else if (fnKey === '$dateCeil') {
|
||||||
|
// $dateCeil: [date, unit]
|
||||||
|
(0, assert_1.default)(fnArgs instanceof Array && fnArgs.length === 2);
|
||||||
|
const [dateExpr, unit] = fnArgs;
|
||||||
|
const getTimestampExpr = (arg) => {
|
||||||
|
if (arg instanceof Date) {
|
||||||
|
return `${arg.valueOf()}`;
|
||||||
|
}
|
||||||
|
else if (typeof arg === 'number') {
|
||||||
|
return `${arg}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const k = Object.keys(arg);
|
||||||
|
if (k.includes('#attr')) {
|
||||||
|
return `\`${alias}\`.\`${arg['#attr']}\``;
|
||||||
|
}
|
||||||
|
else if (k.includes('#refId')) {
|
||||||
|
const refId = arg['#refId'];
|
||||||
|
const refAttr = arg['#refAttr'];
|
||||||
|
return `\`${refDict[refId][0]}\`.\`${refAttr}\``;
|
||||||
|
}
|
||||||
|
return translateInner(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const tsExpr = getTimestampExpr(dateExpr);
|
||||||
|
const msPerUnit = {
|
||||||
|
's': 1000,
|
||||||
|
'm': 60000,
|
||||||
|
'h': 3600000,
|
||||||
|
'd': 86400000,
|
||||||
|
};
|
||||||
|
if (msPerUnit[unit]) {
|
||||||
|
// CEIL 向上取整
|
||||||
|
result = `CEIL(${tsExpr} / ${msPerUnit[unit]}) * ${msPerUnit[unit]}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(0, assert_1.default)(typeof unit === 'string', 'unit should be string');
|
||||||
|
console.warn('暂不支持 $dateCeil 对月年单位的处理');
|
||||||
|
throw new Error(`Unsupported date ceil unit: ${unit}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fnKey === '$dateFloor') {
|
||||||
|
// $dateFloor: [date, unit]
|
||||||
|
(0, assert_1.default)(fnArgs instanceof Array && fnArgs.length === 2);
|
||||||
|
const [dateExpr, unit] = fnArgs;
|
||||||
|
// 获取毫秒时间戳表达式
|
||||||
|
const getTimestampExpr = (arg) => {
|
||||||
|
if (arg instanceof Date) {
|
||||||
|
return `${arg.valueOf()}`;
|
||||||
|
}
|
||||||
|
else if (typeof arg === 'number') {
|
||||||
|
return `${arg}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 属性引用,直接返回属性(存储本身就是毫秒时间戳)
|
||||||
|
const k = Object.keys(arg);
|
||||||
|
if (k.includes('#attr')) {
|
||||||
|
return `\`${alias}\`.\`${arg['#attr']}\``;
|
||||||
|
}
|
||||||
|
else if (k.includes('#refId')) {
|
||||||
|
const refId = arg['#refId'];
|
||||||
|
const refAttr = arg['#refAttr'];
|
||||||
|
return `\`${refDict[refId][0]}\`.\`${refAttr}\``;
|
||||||
|
}
|
||||||
|
// 其他表达式递归处理
|
||||||
|
return translateInner(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const tsExpr = getTimestampExpr(dateExpr);
|
||||||
|
// 固定间隔单位:直接用时间戳数学运算
|
||||||
|
const msPerUnit = {
|
||||||
|
's': 1000,
|
||||||
|
'm': 60000,
|
||||||
|
'h': 3600000,
|
||||||
|
'd': 86400000,
|
||||||
|
};
|
||||||
|
if (msPerUnit[unit]) {
|
||||||
|
// FLOOR(timestamp / interval) * interval
|
||||||
|
result = `FLOOR(${tsExpr} / ${msPerUnit[unit]}) * ${msPerUnit[unit]}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(0, assert_1.default)(typeof unit === 'string', 'unit should be string');
|
||||||
|
console.warn('暂不支持 $dateFloor 对月年单位的处理');
|
||||||
|
throw new Error(`Unsupported date floor unit: ${unit}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fnArgs instanceof Array) {
|
||||||
|
// 原有的数组参数处理逻辑
|
||||||
|
const fnName = this.translateFnName(fnKey, fnArgs.length);
|
||||||
const args = [fnName];
|
const args = [fnName];
|
||||||
args.push(...(expr)[k[0]].map((ele) => {
|
args.push(...fnArgs.map((ele) => {
|
||||||
if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
|
if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
|
||||||
return translateConstant(ele);
|
return translateConstant(ele);
|
||||||
}
|
}
|
||||||
|
|
@ -873,9 +1015,10 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
|
||||||
result = util_1.format.apply(null, args);
|
result = util_1.format.apply(null, args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const fnName = this.translateFnName(k[0], 1);
|
// 原有的单参数处理逻辑
|
||||||
|
const fnName = this.translateFnName(fnKey, 1);
|
||||||
const args = [fnName];
|
const args = [fnName];
|
||||||
const arg = (expr)[k[0]];
|
const arg = fnArgs;
|
||||||
if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
|
if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
|
||||||
args.push(translateConstant(arg));
|
args.push(translateConstant(arg));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -885,10 +885,13 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
||||||
case '$dayOfYear': {
|
case '$dayOfYear': {
|
||||||
return 'DAYOFYEAR(%s)';
|
return 'DAYOFYEAR(%s)';
|
||||||
}
|
}
|
||||||
case '$dateDiff': {
|
|
||||||
assert(argumentNumber === 3);
|
// 这个实现有问题,DATEDIFF只是计算两个日期之间的天数差,只接受两个参数,放在translateExperession里实现
|
||||||
return 'DATEDIFF(%s, %s, %s)';
|
// case '$dateDiff': {
|
||||||
}
|
// assert(argumentNumber === 3);
|
||||||
|
// return 'DATEDIFF(%s, %s, %s)';
|
||||||
|
// }
|
||||||
|
|
||||||
case '$contains': {
|
case '$contains': {
|
||||||
assert(argumentNumber === 2);
|
assert(argumentNumber === 2);
|
||||||
return 'ST_CONTAINS(%s, %s)';
|
return 'ST_CONTAINS(%s, %s)';
|
||||||
|
|
@ -905,6 +908,24 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
||||||
result += ')';
|
result += ')';
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== 聚合函数 ==========
|
||||||
|
case '$$count': {
|
||||||
|
return 'COUNT(%s)';
|
||||||
|
}
|
||||||
|
case '$$sum': {
|
||||||
|
return 'SUM(%s)';
|
||||||
|
}
|
||||||
|
case '$$max': {
|
||||||
|
return 'MAX(%s)';
|
||||||
|
}
|
||||||
|
case '$$min': {
|
||||||
|
return 'MIN(%s)';
|
||||||
|
}
|
||||||
|
case '$$avg': {
|
||||||
|
return 'AVG(%s)';
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
throw new Error(`unrecoganized function ${fnName}`);
|
throw new Error(`unrecoganized function ${fnName}`);
|
||||||
}
|
}
|
||||||
|
|
@ -951,10 +972,136 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert(k.length === 1);
|
assert(k.length === 1);
|
||||||
if ((expr)[k[0]] instanceof Array) {
|
const fnKey = k[0];
|
||||||
const fnName = this.translateFnName(k[0], (expr)[k[0]].length);
|
const fnArgs = (expr)[fnKey];
|
||||||
|
|
||||||
|
// 特殊处理日期相关函数
|
||||||
|
if (fnKey === '$dateDiff') {
|
||||||
|
// $dateDiff: [date1, date2, unit]
|
||||||
|
assert(fnArgs instanceof Array && fnArgs.length === 3);
|
||||||
|
const [date1Expr, date2Expr, unit] = fnArgs;
|
||||||
|
|
||||||
|
// 转换日期表达式
|
||||||
|
const translateDateArg = (arg: any): string => {
|
||||||
|
if (arg instanceof Date) {
|
||||||
|
return `FROM_UNIXTIME(${arg.valueOf()} / 1000)`;
|
||||||
|
} else if (typeof arg === 'number') {
|
||||||
|
return `FROM_UNIXTIME(${arg} / 1000)`;
|
||||||
|
} else {
|
||||||
|
return translateInner(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const date1Str = translateDateArg(date1Expr);
|
||||||
|
const date2Str = translateDateArg(date2Expr);
|
||||||
|
|
||||||
|
// MySQL TIMESTAMPDIFF 单位映射
|
||||||
|
const unitMap: Record<string, string> = {
|
||||||
|
's': 'SECOND',
|
||||||
|
'm': 'MINUTE',
|
||||||
|
'h': 'HOUR',
|
||||||
|
'd': 'DAY',
|
||||||
|
'M': 'MONTH',
|
||||||
|
'y': 'YEAR'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mysqlUnit = unitMap[unit];
|
||||||
|
if (!mysqlUnit) {
|
||||||
|
throw new Error(`Unsupported date diff unit: ${unit}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TIMESTAMPDIFF(unit, date1, date2) 返回 date2 - date1
|
||||||
|
// 但类型定义是 date1 - date2,所以参数顺序要反过来
|
||||||
|
result = `TIMESTAMPDIFF(${mysqlUnit}, ${date2Str}, ${date1Str})`;
|
||||||
|
} else if (fnKey === '$dateCeil') {
|
||||||
|
// $dateCeil: [date, unit]
|
||||||
|
assert(fnArgs instanceof Array && fnArgs.length === 2);
|
||||||
|
const [dateExpr, unit] = fnArgs;
|
||||||
|
|
||||||
|
const getTimestampExpr = (arg: any): string => {
|
||||||
|
if (arg instanceof Date) {
|
||||||
|
return `${arg.valueOf()}`;
|
||||||
|
} else if (typeof arg === 'number') {
|
||||||
|
return `${arg}`;
|
||||||
|
} else {
|
||||||
|
const k = Object.keys(arg);
|
||||||
|
if (k.includes('#attr')) {
|
||||||
|
return `\`${alias}\`.\`${arg['#attr']}\``;
|
||||||
|
} else if (k.includes('#refId')) {
|
||||||
|
const refId = arg['#refId'];
|
||||||
|
const refAttr = arg['#refAttr'];
|
||||||
|
return `\`${refDict[refId][0]}\`.\`${refAttr}\``;
|
||||||
|
}
|
||||||
|
return translateInner(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tsExpr = getTimestampExpr(dateExpr);
|
||||||
|
|
||||||
|
const msPerUnit: Record<string, number> = {
|
||||||
|
's': 1000,
|
||||||
|
'm': 60000,
|
||||||
|
'h': 3600000,
|
||||||
|
'd': 86400000,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (msPerUnit[unit]) {
|
||||||
|
// CEIL 向上取整
|
||||||
|
result = `CEIL(${tsExpr} / ${msPerUnit[unit]}) * ${msPerUnit[unit]}`;
|
||||||
|
} else {
|
||||||
|
assert(typeof unit === 'string', 'unit should be string');
|
||||||
|
console.warn('暂不支持 $dateCeil 对月年单位的处理');
|
||||||
|
throw new Error(`Unsupported date ceil unit: ${unit}`);
|
||||||
|
}
|
||||||
|
} else if (fnKey === '$dateFloor') {
|
||||||
|
// $dateFloor: [date, unit]
|
||||||
|
assert(fnArgs instanceof Array && fnArgs.length === 2);
|
||||||
|
const [dateExpr, unit] = fnArgs;
|
||||||
|
|
||||||
|
// 获取毫秒时间戳表达式
|
||||||
|
const getTimestampExpr = (arg: any): string => {
|
||||||
|
if (arg instanceof Date) {
|
||||||
|
return `${arg.valueOf()}`;
|
||||||
|
} else if (typeof arg === 'number') {
|
||||||
|
return `${arg}`;
|
||||||
|
} else {
|
||||||
|
// 属性引用,直接返回属性(存储本身就是毫秒时间戳)
|
||||||
|
const k = Object.keys(arg);
|
||||||
|
if (k.includes('#attr')) {
|
||||||
|
return `\`${alias}\`.\`${arg['#attr']}\``;
|
||||||
|
} else if (k.includes('#refId')) {
|
||||||
|
const refId = arg['#refId'];
|
||||||
|
const refAttr = arg['#refAttr'];
|
||||||
|
return `\`${refDict[refId][0]}\`.\`${refAttr}\``;
|
||||||
|
}
|
||||||
|
// 其他表达式递归处理
|
||||||
|
return translateInner(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tsExpr = getTimestampExpr(dateExpr);
|
||||||
|
|
||||||
|
// 固定间隔单位:直接用时间戳数学运算
|
||||||
|
const msPerUnit: Record<string, number> = {
|
||||||
|
's': 1000,
|
||||||
|
'm': 60000,
|
||||||
|
'h': 3600000,
|
||||||
|
'd': 86400000,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (msPerUnit[unit]) {
|
||||||
|
// FLOOR(timestamp / interval) * interval
|
||||||
|
result = `FLOOR(${tsExpr} / ${msPerUnit[unit]}) * ${msPerUnit[unit]}`;
|
||||||
|
} else {
|
||||||
|
assert(typeof unit === 'string', 'unit should be string');
|
||||||
|
console.warn('暂不支持 $dateFloor 对月年单位的处理');
|
||||||
|
throw new Error(`Unsupported date floor unit: ${unit}`);
|
||||||
|
}
|
||||||
|
} else if (fnArgs instanceof Array) {
|
||||||
|
// 原有的数组参数处理逻辑
|
||||||
|
const fnName = this.translateFnName(fnKey, fnArgs.length);
|
||||||
const args = [fnName];
|
const args = [fnName];
|
||||||
args.push(...(expr)[k[0]].map(
|
args.push(...fnArgs.map(
|
||||||
(ele: any) => {
|
(ele: any) => {
|
||||||
if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
|
if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
|
||||||
return translateConstant(ele);
|
return translateConstant(ele);
|
||||||
|
|
@ -968,9 +1115,10 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
||||||
result = format.apply(null, args);
|
result = format.apply(null, args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const fnName = this.translateFnName(k[0], 1);
|
// 原有的单参数处理逻辑
|
||||||
|
const fnName = this.translateFnName(fnKey, 1);
|
||||||
const args = [fnName];
|
const args = [fnName];
|
||||||
const arg = (expr)[k[0]];
|
const arg = fnArgs;
|
||||||
if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
|
if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
|
||||||
args.push(translateConstant(arg));
|
args.push(translateConstant(arg));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue