{"record":{"id":"9d2aa5aaa2e812b3","repo":"hibernate/hibernate-orm","slug":"scalar-multiplication-of-temporal-value","errorCode":null,"errorMessage":"Scalar multiplication of temporal value","messagePattern":"Scalar multiplication of temporal value","errorType":"exception","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java","lineNumber":7089,"sourceCode":"\n\t\t// Need to infer the operand types here first to decide how to transform the expression\n\t\tfinal var fromClauseIndex = fromClauseIndexStack.getCurrent();\n\t\tinferrableTypeAccessStack.push( () -> determineValueMapping( rightOperand, fromClauseIndex ) );\n\t\tfinal var leftOperandType = determineValueMapping( leftOperand );\n\t\tinferrableTypeAccessStack.pop();\n\t\tinferrableTypeAccessStack.push( () -> determineValueMapping( leftOperand, fromClauseIndex ) );\n\t\tfinal var rightOperandType = determineValueMapping( rightOperand );\n\t\tinferrableTypeAccessStack.pop();\n\n\t\tfinal boolean durationToRight = isDuration( rightOperand.getNodeType() );\n\t\tfinal var temporalTypeToLeft = getSqlTemporalType( leftOperandType );\n\t\tfinal var temporalTypeToRight = getSqlTemporalType( rightOperandType );\n\t\tfinal boolean temporalTypeSomewhereToLeft = adjustedTimestamp != null || temporalTypeToLeft != null;\n\n\t\tif ( temporalTypeToLeft != null && durationToRight ) {\n\t\t\tif ( adjustmentScale != null || negativeAdjustment ) {\n\t\t\t\t//we can't distribute a scale over a date/timestamp\n\t\t\t\tthrow new SemanticException( \"Scalar multiplication of temporal value\" );\n\t\t\t}\n\t\t}\n\n\t\tif ( durationToRight && temporalTypeSomewhereToLeft ) {\n\t\t\treturn transformDurationArithmetic( expression );\n\t\t}\n\t\telse if ( temporalTypeToLeft != null && temporalTypeToRight != null ) {\n\t\t\treturn transformDatetimeArithmetic( expression );\n\t\t}\n\t\telse {\n\t\t\t// Infer one operand type through the other\n\t\t\tinferrableTypeAccessStack.push( () -> determineValueMapping( rightOperand, fromClauseIndex ) );\n\t\t\tfinal var lhs = toSqlExpression( leftOperand.accept( this ) );\n\t\t\tinferrableTypeAccessStack.pop();\n\t\t\tinferrableTypeAccessStack.push( () -> determineValueMapping( leftOperand, fromClauseIndex ) );\n\t\t\tfinal var rhs = toSqlExpression( rightOperand.accept( this ) );\n\t\t\tinferrableTypeAccessStack.pop();\n","sourceCodeStart":7071,"sourceCodeEnd":7107,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java#L7071-L7107","documentation":"In binary arithmetic translation, the left operand resolved to a temporal type and the right to a duration, but a scale was already pending (adjustmentScale non-null) or a negation was queued. SQL cannot distribute a multiplication or division over a date/timestamp - only durations may be scaled - so the SemanticException rejects it.","triggerScenarios":"HQL like 'current_timestamp * 2', 'e.eventTs * :factor', or expressions where a temporal value ends up on the left of a scaled duration operation such as '2 * (ts - other) * 3' after distribution.","commonSituations":"Porting date math from languages/databases that allow scaling timestamps; report queries computing weighted time offsets; generated queries that wrap whole arithmetic trees in multipliers.","solutions":["Scale the duration, not the timestamp: '(ts1 - ts2) * 2 by second'","Move the multiplier so the temporal value is only ever added to or subtracted from a duration","Use duration literals ('2 day') or functions instead of raw scalar multiplication on temporals"],"exampleFix":"// before\nselect e.ts * 2 from Event e\n\n// after\nselect (e.endTs - e.ts) * 2 from Event e","handlingStrategy":"validation","validationCode":"// In query builders: reject arithmetic where a temporal path is multiplied/divided\nif (isTemporalPath(leftPath) && (operator == '*' || operator == '/')) {\n    throw new IllegalArgumentException(\"Cannot scale a temporal value; scale the duration (ts1 - ts2) instead\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return session.createQuery(hql).getResultList();\n} catch (org.hibernate.query.SemanticException e) {\n    log.error(\"Temporal arithmetic rejected: {}\", hql, e);\n    throw e;\n}","preventionTips":["Apply * and / only to durations, never to timestamps","Write duration math as (end - start) * scale by unit","Add unit tests for every generated temporal expression"],"tags":["hibernate","hql","temporal","arithmetic","duration"],"backgroundTag":"temporal-arithmetic-unsupported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}