公开文档第五节推荐的 dsl({model: prevPlan, ...}) 派生写法,在 Launcher 0.1.39 的 Runtime 受限 Compose 中触发 Java ClassCastException。基础计划 BaseModelPlan 与 JOIN 计划 JoinPlan 均复现;相同投影改用 prevPlan.query({...}) 可以通过验证、预览并执行。
归属:Java Runtime Compose 的 dsl 宿主桥接与公开文档合同一致性,不是数据库 SQL 执行错误。校验错误还应明确输入类型 / 参数位置,相关诊断方向见 #160。
7b6d06987e3ec90f05143a0552791f80381dcc4b,Java projectVersion:9.3.0-SNAPSHOT;不是本地源码编译结果。8228d31c104d58b1b2f1d1c5bc856a25531e4d5af06e1e9eefed2be576f20bf9。foggy-runtime 0.1.25;CLI 子进程使用 UTF-8 输出。http://127.0.0.1:18067;Namespace:chinook-composition;命名数据源:chinook-composition-sqlite(SQLite,公开 Chinook v1.4.5 副本)。securityMode=none-dev-test-only,匿名本地教学环境,Console/FAP 未启用;未使用生产数据、身份、私有接口或 SQL 直通。capabilities 实际响应节选:
{
"engine": "java",
"runtimeApiVersion": "foggy-runtime-api/v1",
"schemaVersion": "2026-06-06",
"securityMode": "none-dev-test-only",
"capabilities": {
"compose.validate": "supported",
"compose.preview": "supported",
"compose.execute": "supported",
"models.describe": "supported"
}
}
ChinookInvoiceQueryModel 对应 Chinook 的 Invoice 表,必要公开字段如下:
| 字段 | 实际物理映射 / 角色 |
|---|---|
| customerId | Invoice.CustomerId,客户键 |
| invoiceDate | Invoice.InvoiceDate,账单日期 |
| invoiceAmount | Invoice.Total,MONEY、sum;沿用样例原单位 |
下面只引用已公开字段和前一计划的输出别名。年度基线为 47 位有账单客户、83 张账单、477.53;原始数据与模型在复现前后均未修改。
已远端读取核对 DSL 手册第五节,该节标题为“派生查询:dsl({ model: prevPlan, ... })”,并明确 model 可传上一个 QueryPlan。本轮读取文件的 Git blob SHA 为 60ec0347b097cd2887cbcd3c9008c270b700054a。
02-dsl-plan-model.fsscript。它只把上一计划 bills 作为 dsl 的 model,没有 calculatedFields,也没有 JOIN:const bills = dsl({
model: 'ChinookInvoiceQueryModel',
columns: ['customerId as customer_id', 'sum(invoiceAmount) as bill_amount'],
slice: [{field: 'invoiceDate', op: '[)', value: ['2012-01-01', '2013-01-01']}],
groupBy: ['customerId']
});
return {plans: dsl({
model: bills,
columns: ['customer_id', 'bill_amount'],
orderBy: ['customer_id'], limit: 100
})};
foggy-runtime --output json --base-url http://127.0.0.1:18067 --namespace chinook-composition compose validate --script ./02-dsl-plan-model.fsscript
按公开文档将 bills 视作计划输入,对其已输出的 customer_id、bill_amount 做投影,校验应通过。
如果标准 Runtime 的受限桥接有意不支持该公开形态,至少应在输入校验中返回明确的 model 类型 / 能力限制及路径,并给出 bills.query({...}) 替代写法,同时对齐文档的宿主适用范围;不应暴露内部 Java 强制类型转换异常。
CLI exitCode 2,success=false,error.code=COMPOSE_SCRIPT_INVALID、phase=compose.validate、safeToAutoRepair=false。完整实际响应:
{
"diagnostics": {
"attributes": {
"datasourceType": "sqlite",
"defaultDialect": "sqlite",
"dialectSource": "namespace-datasource-type",
"namespace": "chinook-composition",
"namespaceDatasourceId": "chinook-composition-sqlite",
"namespaceDatasourceStatus": "active",
"resolvedDialect": "sqlite"
},
"warnings": []
},
"engine": "java",
"error": {
"code": "COMPOSE_SCRIPT_INVALID",
"message": "class com.foggyframework.dataset.model.engine.compose.plan.BaseModelPlan cannot be cast to class java.lang.String (com.foggyframework.dataset.model.engine.compose.plan.BaseModelPlan is in unnamed module of loader org.springframework.boot.loader.launch.LaunchedClassLoader @7106e68e; java.lang.String is in module java.base of loader 'bootstrap')",
"phase": "compose.validate",
"safeToAutoRepair": false,
"suggestedNextAction": "Inspect diagnostics and runtime logs, then retry."
},
"runtimeApiVersion": "foggy-runtime-api/v1",
"success": false
}
失败发生在脚本宿主构造计划时,尚未执行数据库查询;没有调用 execute。
以下为本次复现的 runtime.out.log 增量节选:
java.lang.ClassCastException: class com.foggyframework.dataset.model.engine.compose.plan.BaseModelPlan cannot be cast to class java.lang.String (com.foggyframework.dataset.model.engine.compose.plan.BaseModelPlan is in unnamed module of loader org.springframework.boot.loader.launch.LaunchedClassLoader @7106e68e; java.lang.String is in module java.base of loader 'bootstrap')
at com.foggyframework.dataset.model.engine.compose.runtime.ScriptRuntime.lambda$doRunScript$0(ScriptRuntime.java:276)
java.lang.ClassCastException: class com.foggyframework.dataset.model.engine.compose.plan.JoinPlan cannot be cast to class java.lang.String (com.foggyframework.dataset.model.engine.compose.plan.JoinPlan is in unnamed module of loader org.springframework.boot.loader.launch.LaunchedClassLoader @7106e68e; java.lang.String is in module java.base of loader 'bootstrap')
at com.foggyframework.dataset.model.engine.compose.runtime.ScriptRuntime.lambda$doRunScript$0(ScriptRuntime.java:276)
实际完整请求:
const bills = dsl({
model: 'ChinookInvoiceQueryModel',
columns: ['customerId as customer_id', 'sum(invoiceAmount) as bill_amount'],
slice: [{field: 'invoiceDate', op: '[)', value: ['2012-01-01', '2013-01-01']}],
groupBy: ['customerId']
});
return {plans: bills.query({
columns: ['customer_id', 'bill_amount'],
orderBy: ['customer_id'], limit: 100
})};
这个请求 validate、preview、execute 退出码均为 0,实际得到 47 位客户 / 477.53;没有更换模型、权限、数据源或日期条件。
为避免与另一单计算字段问题混淆,本轮另用纯列内表达式的 JOIN 请求复测 model: joined。完整实际请求与响应:
const bills = dsl({
model: 'ChinookInvoiceQueryModel',
columns: ['customerId as bill_customer_id', 'sum(invoiceAmount) as bill_amount'],
slice: [{ field: 'invoiceDate', op: '[)', value: ['2012-01-01', '2013-01-01'] }],
groupBy: ['customerId']
});
const lines = dsl({
model: 'ChinookInvoiceLineQueryModel',
columns: ['invoice$customerId as line_customer_id', 'sum(lineAmount) as line_amount'],
slice: [{ field: 'invoice$invoiceDate', op: '[)', value: ['2012-01-01', '2013-01-01'] }],
groupBy: ['invoice$customerId']
});
const joined = bills.join(lines, 'left', [
{ left: 'bill_customer_id', op: '=', right: 'line_customer_id' }
]);
const compared = dsl({
model: joined,
columns: ['bill_customer_id', 'line_customer_id', 'bill_amount', 'line_amount',
'ROUND(bill_amount - line_amount, 2) as amount_difference'],
orderBy: ['bill_customer_id'], limit: 100
});
return { plans: compared };
{
"diagnostics": {
"attributes": {
"datasourceType": "sqlite",
"defaultDialect": "sqlite",
"dialectSource": "namespace-datasource-type",
"namespace": "chinook-composition",
"namespaceDatasourceId": "chinook-composition-sqlite",
"namespaceDatasourceStatus": "active",
"resolvedDialect": "sqlite"
},
"warnings": []
},
"engine": "java",
"error": {
"code": "COMPOSE_SCRIPT_INVALID",
"message": "class com.foggyframework.dataset.model.engine.compose.plan.JoinPlan cannot be cast to class java.lang.String (com.foggyframework.dataset.model.engine.compose.plan.JoinPlan is in unnamed module of loader org.springframework.boot.loader.launch.LaunchedClassLoader @7106e68e; java.lang.String is in module java.base of loader 'bootstrap')",
"phase": "compose.validate",
"safeToAutoRepair": false,
"suggestedNextAction": "Inspect diagnostics and runtime logs, then retry."
},
"runtimeApiVersion": "foggy-runtime-api/v1",
"success": false
}
上述失败请求均止于 validate。当前教学主线使用已通过的 plan.query(...),没有用私有 API 或手写 SQL 绕过语义治理。