judge가 로컬 MySQL 템플릿 형식의 init SQL을 받아들이도록 수정 - #53
Conversation
Re-grading last semester's 61 challenges on the MySQL judge failed 58 of them at init: the scripts are MySQL Workbench and mysqldump exports that manage their own schema and session. - Skip CREATE/DROP DATABASE|SCHEMA and USE statements and map that schema name onto the execution's temporary database, including mysqldump's /*!... */ wrapped forms and schema-qualified names such as kbo.PLAYER in init and graded statements. - Drop leading comment lines from each statement; MySQL rejects "-----" separators and empty comment-only chunks. - Carry the init session's sql_mode (for example TRADITIONAL) into the graded statement's session, as one local MySQL session would. - Start MySQL with lower_case_table_names=1 so Salaries and salaries match, as on Windows and macOS where the challenges were written. The re-grade tool now treats float-precision differences as formatting.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5a9191511b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| pattern := regexp.MustCompile("(?i)(^|[^A-Za-z0-9_$.`])`?" + regexp.QuoteMeta(alias) + "`?\\s*\\.\\s*(`?[A-Za-z0-9_$]+`?)") | ||
| statement = pattern.ReplaceAllString(statement, "${1}"+quoteIdentifier(database)+".${2}") |
There was a problem hiding this comment.
Keep schema alias rewriting out of SQL literals
When an init script declares an alias such as company, this regex also rewrites occurrences like 'company.com' inside string literals. Because the solution and submission are evaluated in separately named temporary databases, an init row containing that value becomes a different random string in each evaluation, so even identical queries that return the row are marked as mismatches. Rewrite only parsed schema-qualified identifiers, not raw SQL text.
Useful? React with 👍 / 👎.
| // with their own schema name. These statements are mapped onto the | ||
| // temporary database instead of being executed. | ||
| schemaStatementPattern = regexp.MustCompile("(?is)^(?:CREATE\\s+(?:DATABASE|SCHEMA)(?:\\s+IF\\s+NOT\\s+EXISTS)?|DROP\\s+(?:DATABASE|SCHEMA)(?:\\s+IF\\s+EXISTS)?|USE)\\s+`?([A-Za-z0-9_$]+)`?(?:\\s|$)") | ||
| sqlModePattern = regexp.MustCompile(`^[A-Za-z0-9_,]*$`) |
There was a problem hiding this comment.
Accept valid quoted schema identifiers
For valid exported schemas whose quoted name contains a hyphen, space, Unicode character, or another character outside [A-Za-z0-9_$]—for example USE kbo-data``—this pattern does not recognize the statement. It is consequently executed rather than mapped to the temporary database and fails with the same access-denied error this change is intended to eliminate.
Useful? React with 👍 / 👎.
| """Equal as numbers, allowing for DECIMAL vs float text precision (72220.1111 vs 72220.11111111111).""" | ||
| try: | ||
| return float(left) == float(right) | ||
| return math.isclose(float(left), float(right), rel_tol=1e-6, abs_tol=1e-9) |
There was a problem hiding this comment.
Do not classify materially different numbers as formatting
With rel_tol=1e-6, distinct integral results such as 1000000 and 1000001 satisfy math.isclose and are classified as numeric_format; summarize then treats that comparison as successful and exits zero. This can hide real judge regressions during the full regrade rather than merely tolerating DECIMAL-versus-float textual precision differences.
Useful? React with 👍 / 👎.
dev 배포 결과 (2026-09-02)
지난 학기 문제 61개를
같은 인스턴스의 부하 실측(PR #52 병합 직후 release 기준)은 5테이블×100행 init에서 동시 16에 7.2 req/s·p95 2.5초, 동시 32에서 26/96이 busy 응답, 5테이블×1000행 init은 동시 16에 3.9 req/s·p95 4.7초였습니다. CTFd 웹 경로(setup → 관리자 로그인 → SQL 문제 생성 → Test 5종 → Submit 2종)도 dev에서 정상 동작했습니다. |
목적
PR #52를
dev에 병합하고sql-dev.ddps.cloud에 배포한 뒤, 지난 학기 문제 61개를scripts/regrade-challenges로 전수 재채점했더니 58개가 init 단계에서Error 1044 Access denied for user 'ct_…'@'%' to database 'kbo'로 실패했습니다. 옛 go-mysql-server judge는 61개 모두 통과했습니다.원인
문제 init SQL 대부분이 MySQL Workbench·mysqldump 템플릿을 그대로 쓰고 있습니다.
새 judge는 실행마다
ctfd_tmp_<hex>하나에만 권한을 주므로DROP/CREATE SCHEMA kbo와USE kbo가 거부됩니다. 옛 엔진은 프로세스 안의 인메모리 DB라 아무 이름이나 만들 수 있었습니다. 또한 옛 엔진과 로컬 MySQL에서는 init과 채점 문장이 같은 세션이라 init의SET SQL_MODE='TRADITIONAL'(ONLY_FULL_GROUP_BY 없음)이 정답 쿼리에도 적용됐는데, 새 judge는 init 계정과 채점 계정의 세션이 분리되어 서버 기본 sql_mode로 실행했습니다.변경
CREATE DATABASE|SCHEMA,DROP DATABASE|SCHEMA,USE는 실행하지 않고 그 스키마 이름을 임시 DB의 별칭으로 기억합니다. 앞선 주석(--,/* */)은 건너뛰고 판별하며,/*! */버전 주석은 그대로 실행합니다.kbo.PLAYER)은 init 문장과 채점 문장 모두에서 임시 DB 이름으로 바꿉니다.@@SESSION.sql_mode를 읽어 채점 세션에 같은 값을 적용합니다. init이 없으면 서버 기본값 그대로입니다.SET문이 끝까지 유지되게 했습니다.검증
CREATE TABLE kbo, 문자열 안의USE), 별칭 치환.kbo.PLAYER에 대한GROUP BY비집계 SELECT가 TRADITIONAL 모드로 성공, 실제kbo스키마는 생성되지 않음, init 없는 실행은 여전히ONLY_FULL_GROUP_BY적용.전수 재채점 결과 (로컬, 수정된 judge vs
dev@72b1775d의 옛 judge)183.0901vs183.0900900900901,8510700.00vs8510700,4017733vs4.017733e+06)#22,#23,#27,#30,#36,#57)2024-03-23vs 옛2024-03-23 00:00:00 +0000 UTC)#24,#34)#32:GROUP BY TEAM_ID가 두 테이블에 모두 있어 MySQL이 ambiguous로 거부. 로컬 MySQL에서도 같은 오류)행 순서 차이는 두 엔진 중 어느 쪽이 맞다고 할 수 없는 문제 정의 이슈이며 지난 학기 DDPS-791에서 반복 지적된 tie-break 항목입니다. 숫자·DATE 표현 차이는 정답과 제출이 같은 엔진에서 실행되므로 채점에는 영향이 없고, 지문의 예시 출력만 갱신하면 됩니다.
#33(팀별 최장신, 상관 서브쿼리 안의 GROUP BY)은 로컬에서 1.2초로 가장 느려 t4g.small에서 실행당 2.5초 예산을 확인해야 합니다.문제 SQL 자체는 저장소에 넣지 않았습니다.