feat(SQL 题): 增删改题除了第 1 组,其余测试数据可以一行都不改——用来抓学生多改(WHERE 写漏、写宽)
Deploy / deploy (push) Canceled after 0s
Deploy / deploy (push) Canceled after 0s
原来每组数据都必须有被标准答案改到的行,所以「这组里没有研发部,看学生会不会把别的部门也改了」 这种测试数据建不了。现在只有第 1 组(题面上给学生看的那组)还要求改到行;其余组一行都没改时 出题页写「一行都没改」、预览写「执行后所有表都没有变化」。删掉第 1 组、别的组顶上来时自动按 新位置重查。判题本来就不管这一条,不受影响。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2cb39fd96b
commit
033a245abb
9 files changed
+54
-9
No files matched your search
@@ -34,6 +34,7 @@ export type SqlJob =
|
||||
initSql: string
|
||||
refSql: string
|
||||
mode: "query" | "modify"
|
||||
requireChange: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +77,13 @@ export async function runSqlChild() {
|
||||
try {
|
||||
if (job.kind === "display") {
|
||||
markPhase("display")
|
||||
const display = await buildDisplay(job.initSql, job.refSql, job.mode)
|
||||
const display = await buildDisplay(
|
||||
job.initSql,
|
||||
job.refSql,
|
||||
job.mode,
|
||||
undefined,
|
||||
job.requireChange,
|
||||
)
|
||||
finish({ ok: true, display })
|
||||
}
|
||||
// 阶段由 runCase 内部回调标记:prepare(受信脚本)→ student(学生 SQL)
|
||||
|
||||
@@ -584,12 +584,19 @@ function queryResultColumns(names: string[], tables: DisplayTable[]) {
|
||||
return names.map((name) => ({ name, type: types.get(name) ?? "" }))
|
||||
}
|
||||
|
||||
/** 生成题目页展示数据:初始数据表 + 期望结果。失败一律抛 SqlCaseError(出题配置问题) */
|
||||
/**
|
||||
* 生成题目页展示数据:初始数据表 + 期望结果。失败一律抛 SqlCaseError(出题配置问题)。
|
||||
*
|
||||
* `requireChange`:增删改题的标准答案必须改到行。题面上展示的那组(第 1 组)必须 ——
|
||||
* 学生要看着「执行后的表」知道该改什么;其余组可以一行都不改(看学生会不会多改),
|
||||
* 这时 changed_tables 是空数组。判题(runCase)本来就不管这一条
|
||||
*/
|
||||
export async function buildDisplay(
|
||||
initSql: string,
|
||||
refSql: string,
|
||||
mode: "query" | "modify",
|
||||
memoryLimitMb = 64,
|
||||
requireChange = true,
|
||||
) {
|
||||
const SQL = await sqlEngine()
|
||||
const db = newDatabase(SQL, memoryLimitMb)
|
||||
@@ -648,6 +655,7 @@ export async function buildDisplay(
|
||||
if (JSON.stringify(before[name]) !== JSON.stringify(after[name])) changed.add(name)
|
||||
}
|
||||
if (changed.size === 0) {
|
||||
if (!requireChange) return { tables, expected: { changed_tables: [] } }
|
||||
throw new SqlCaseError(JudgeStatus.SYSTEM_ERROR, "标准答案未修改任何表数据,请检查题目配置")
|
||||
}
|
||||
const changedTables = dumpDisplayTables(db, changed)
|
||||
|
||||
@@ -184,11 +184,17 @@ export function runSqlCase(job: Extract<SqlJob, { kind: "judge" }>) {
|
||||
})
|
||||
}
|
||||
|
||||
export function buildSqlDisplay(initSql: string, refSql: string, mode: "query" | "modify") {
|
||||
/** `requireChange` 见 engine.ts 的 buildDisplay:只有题面上展示的那组必须改到行 */
|
||||
export function buildSqlDisplay(
|
||||
initSql: string,
|
||||
refSql: string,
|
||||
mode: "query" | "modify",
|
||||
requireChange = true,
|
||||
) {
|
||||
// 子进程产出的形状由 engine.ts 的 dumpDisplayTables / runDisplay 决定,就是契约里的
|
||||
// SqlDisplay —— 同一个仓库里的两端,不在这儿再 parse 一遍
|
||||
return runJob<SqlDisplay>(
|
||||
{ kind: "display", initSql, refSql, mode },
|
||||
{ kind: "display", initSql, refSql, mode, requireChange },
|
||||
{ trustedMs: DISPLAY_BUDGET_MS, studentMs: null },
|
||||
)
|
||||
}
|
||||
@@ -867,7 +867,12 @@ adminProblemRoutes.get("/problems/:id/sql-scripts", requireProblemPermission, as
|
||||
adminProblemRoutes.post("/sql-test-cases/preview", requireProblemPermission, async (c) => {
|
||||
const parsed = await parseBody(c, sqlPreviewRequestSchema)
|
||||
if (!parsed.success) return parsed.response
|
||||
const outcome = await buildSqlDisplay(parsed.data.initSql, parsed.data.refSql, parsed.data.mode)
|
||||
const outcome = await buildSqlDisplay(
|
||||
parsed.data.initSql,
|
||||
parsed.data.refSql,
|
||||
parsed.data.mode,
|
||||
parsed.data.shown,
|
||||
)
|
||||
if (!outcome.ok) return failure(c, 400, "sql-preview-failed", outcome.message)
|
||||
return success(c, outcome.value)
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user