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': {
|
||||
return 'DAYOFYEAR(%s)';
|
||||
}
|
||||
case '$dateDiff': {
|
||||
(0, assert_1.default)(argumentNumber === 3);
|
||||
return 'DATEDIFF(%s, %s, %s)';
|
||||
}
|
||||
// 这个实现有问题,DATEDIFF只是计算两个日期之间的天数差,只接受两个参数,放在translateExperession里实现
|
||||
// case '$dateDiff': {
|
||||
// assert(argumentNumber === 3);
|
||||
// return 'DATEDIFF(%s, %s, %s)';
|
||||
// }
|
||||
case '$contains': {
|
||||
(0, assert_1.default)(argumentNumber === 2);
|
||||
return 'ST_CONTAINS(%s, %s)';
|
||||
|
|
@ -816,6 +817,22 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
|
|||
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: {
|
||||
throw new Error(`unrecoganized function ${fnName}`);
|
||||
}
|
||||
|
|
@ -859,10 +876,135 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
|
|||
}
|
||||
else {
|
||||
(0, assert_1.default)(k.length === 1);
|
||||
if ((expr)[k[0]] instanceof Array) {
|
||||
const fnName = this.translateFnName(k[0], (expr)[k[0]].length);
|
||||
const fnKey = k[0];
|
||||
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];
|
||||
args.push(...(expr)[k[0]].map((ele) => {
|
||||
args.push(...fnArgs.map((ele) => {
|
||||
if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
|
||||
return translateConstant(ele);
|
||||
}
|
||||
|
|
@ -873,9 +1015,10 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
|
|||
result = util_1.format.apply(null, args);
|
||||
}
|
||||
else {
|
||||
const fnName = this.translateFnName(k[0], 1);
|
||||
// 原有的单参数处理逻辑
|
||||
const fnName = this.translateFnName(fnKey, 1);
|
||||
const args = [fnName];
|
||||
const arg = (expr)[k[0]];
|
||||
const arg = fnArgs;
|
||||
if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
|
||||
args.push(translateConstant(arg));
|
||||
}
|
||||
|
|
@ -949,7 +1092,7 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
|
|||
}
|
||||
// 新的remove应该包含$$deleteAt$$的值了
|
||||
/* const now = Date.now();
|
||||
|
||||
|
||||
const updateText2 = updateText ? `${updateText}, \`${alias}\`.\`$$deleteAt$$\` = '${now}'` : `\`${alias}\`.\`$$deleteAt$$\` = '${now}'`; */
|
||||
(0, assert_1.default)(updateText.includes(types_1.DeleteAtAttribute));
|
||||
let sql = `update ${fromText} set ${updateText}`;
|
||||
|
|
|
|||
|
|
@ -468,7 +468,7 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
|||
}
|
||||
}
|
||||
else {
|
||||
assert (typeof length === 'object');
|
||||
assert(typeof length === 'object');
|
||||
const op = Object.keys(length)[0];
|
||||
assert(op.startsWith('$'));
|
||||
if (p) {
|
||||
|
|
@ -885,10 +885,13 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
|||
case '$dayOfYear': {
|
||||
return 'DAYOFYEAR(%s)';
|
||||
}
|
||||
case '$dateDiff': {
|
||||
assert(argumentNumber === 3);
|
||||
return 'DATEDIFF(%s, %s, %s)';
|
||||
}
|
||||
|
||||
// 这个实现有问题,DATEDIFF只是计算两个日期之间的天数差,只接受两个参数,放在translateExperession里实现
|
||||
// case '$dateDiff': {
|
||||
// assert(argumentNumber === 3);
|
||||
// return 'DATEDIFF(%s, %s, %s)';
|
||||
// }
|
||||
|
||||
case '$contains': {
|
||||
assert(argumentNumber === 2);
|
||||
return 'ST_CONTAINS(%s, %s)';
|
||||
|
|
@ -905,6 +908,24 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
|||
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: {
|
||||
throw new Error(`unrecoganized function ${fnName}`);
|
||||
}
|
||||
|
|
@ -951,10 +972,136 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
|||
}
|
||||
else {
|
||||
assert(k.length === 1);
|
||||
if ((expr)[k[0]] instanceof Array) {
|
||||
const fnName = this.translateFnName(k[0], (expr)[k[0]].length);
|
||||
const fnKey = k[0];
|
||||
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];
|
||||
args.push(...(expr)[k[0]].map(
|
||||
args.push(...fnArgs.map(
|
||||
(ele: any) => {
|
||||
if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
|
||||
return translateConstant(ele);
|
||||
|
|
@ -968,9 +1115,10 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
|||
result = format.apply(null, args);
|
||||
}
|
||||
else {
|
||||
const fnName = this.translateFnName(k[0], 1);
|
||||
// 原有的单参数处理逻辑
|
||||
const fnName = this.translateFnName(fnKey, 1);
|
||||
const args = [fnName];
|
||||
const arg = (expr)[k[0]];
|
||||
const arg = fnArgs;
|
||||
if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
|
||||
args.push(translateConstant(arg));
|
||||
}
|
||||
|
|
@ -1060,7 +1208,7 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
|||
|
||||
// 新的remove应该包含$$deleteAt$$的值了
|
||||
/* const now = Date.now();
|
||||
|
||||
|
||||
const updateText2 = updateText ? `${updateText}, \`${alias}\`.\`$$deleteAt$$\` = '${now}'` : `\`${alias}\`.\`$$deleteAt$$\` = '${now}'`; */
|
||||
assert(updateText.includes(DeleteAtAttribute));
|
||||
let sql = `update ${fromText} set ${updateText}`;
|
||||
|
|
@ -1077,4 +1225,4 @@ export class MySqlTranslator<ED extends EntityDict & BaseEntityDict> extends Sql
|
|||
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue