source: trunk/common/db/job.php @ 1184

Revision 1179, 15.8 KB checked in by adrian.budau, 2 months ago (diff)

Added task security(private, protected, public) and also fixed some
problems like the fact that users could add hidden tasks to user-rounds
during contests or cache crashing during task creation.
Reviewboard URL: http://reviewboard.infoarena.ro/r/203/

  • Property svn:eol-style set to native
Line 
1<?php
2
3require_once(IA_ROOT_DIR."common/db/db.php");
4
5/*function db_format_field_list($fields) {
6    return $result;
7    foreach ($fields as $k => $v) {
8        if ($result != '') {
9            $result .= ', ';
10        }
11        if (is_array($v)) {
12            $result .= "`{$v[0]}`.`{$v[1]}`";
13        } else {
14            $result .= "`{$v}`";
15        }
16        if (is_string($k)) {
17            $result .= " AS `$k`";
18        }
19    }
20    return $result;
21}*/
22
23// Creates new eval job
24function job_create($task_id, $round_id, $user_id, $compiler_id, $file_contents,
25        $remote_ip_info = null) {
26    /**
27     * Check which submission is the current one(first, second, ...)
28     * Counting from 0
29     */
30    $submission = task_user_get_submit_count($user_id, $round_id, $task_id);
31    $query = <<<SQL
32        INSERT INTO ia_job
33            (`task_id`, `round_id`, `user_id`, `compiler_id`, `file_contents`,
34             `submit_time`, `remote_ip_info`, `submissions`)
35        VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
36SQL;
37    $query = sprintf($query,
38            db_quote($task_id), db_quote($round_id), db_quote($user_id),
39            db_quote($compiler_id), db_quote($file_contents),
40            db_quote(db_date_format()), db_quote($remote_ip_info),
41            db_quote($submission));
42
43    /**
44     * Increment the submission count
45     */
46    task_user_update_submit_count($user_id, $round_id, $task_id);
47    return db_query($query);
48}
49
50// Get something for the evaluator to do.
51// Null if nothing is found.
52function job_get_next_job() {
53    $query = <<<SQL
54SELECT `job`.`id`, `job`.`user_id`, `job`.`task_id`, `job`.`round_id`,
55       `job`.`compiler_id`, `job`.`status`, `job`.`submit_time`,
56       `job`.`eval_message`, `job`.`score`, `job`.`file_contents`,
57       `job`.`remote_ip_info`, `job`.`submissions`
58    FROM `ia_job` AS `job`
59    WHERE `job`.`id` = (
60        SELECT MIN(id)
61        FROM `ia_job`
62        WHERE (`status` IN ('waiting', 'processing'))
63    )
64SQL;
65    return db_fetch($query);
66}
67
68// Update job status.
69// Null parameters doesn't update.
70function job_update($job_id, $status = null,  $eval_message = null,
71                    $eval_log = null, $score = null) {
72    log_assert(is_whole_number($job_id));
73
74    // Build set statements.
75    $set_statements = array();
76    if ($status !== null) {
77        log_assert($status == 'processing' ||
78                   $status == 'waiting' ||
79                   $status == 'done', "Invalid status");
80        $set_statements[] = "`status` = '".db_escape($status)."'";
81    }
82    if ($eval_message !== null) {
83        $set_statements[] = "`eval_message` = '".db_escape($eval_message)."'";
84    }
85    if ($eval_log !== null) {
86        $set_statements[] = "`eval_log` = '".db_escape($eval_log)."'";
87    }
88    if ($score !== null) {
89        $set_statements[] = "`score` = '".db_escape($score)."'";
90    }
91    $query = sprintf("UPDATE ia_job SET %s WHERE id = %s",
92            implode(', ', $set_statements), $job_id);
93    db_query($query);
94    return db_affected_rows();
95}
96
97function job_get_by_id($job_id, $contents = false) {
98    log_assert(is_whole_number($job_id));
99    $field_list = "`job`.`id`, job.`user_id`, `job`.`compiler_id`, `job`.`status`,
100                   `job`.`submit_time`, `job`.`eval_message`, `job`.`score`,
101                   `job`.`eval_log`, `job`.`remote_ip_info`, `job`.`submissions`,
102                   OCTET_LENGTH(`job`.`file_contents`) AS `job_size`,
103                   `user`.`username` AS `user_name`, `user`.`full_name` AS `user_fullname`,
104                   `task`.`id` AS `task_id`,
105                   `task`.`page_name` AS `task_page_name`, task.`title` AS `task_title`,
106                   `task`.`security` AS `task_security`,
107                   `task`.`user_id` AS `task_owner_id`,
108                   `task`.`open_source` AS `task_open_source`,
109                   `task`.`open_tests` AS `task_open_tests`,
110                   `round`.`id` AS `round_id`,
111                   `round`.`page_name` AS `round_page_name`,
112                   `round`.`title` AS `round_title`,
113                   `round`.`type` AS `round_type`,
114                   `round`.`state` AS `round_state`,
115                   `round`.`public_eval` AS `round_public_eval`,
116                   `round`.`start_time` AS `round_start_time`";
117    if ($contents) {
118        $field_list .= ", job.file_contents";
119    }
120    $query = sprintf("
121              SELECT $field_list
122              FROM ia_job AS job
123              LEFT JOIN `ia_task` AS `task` ON `job`.`task_id` = `task`.`id`
124              LEFT JOIN `ia_user` AS `user` ON `job`.`user_id` = `user`.`id`
125              LEFT JOIN `ia_round` AS `round` ON `job`.`round_id` = `round`.`id`
126              WHERE `job`.`id` = %d", $job_id);
127    return db_fetch($query);
128}
129
130// Returns a where clause array based on complex filters
131// that relate only to ia_job table
132function job_get_range_wheres_job($filters) {
133    $user = getattr($filters, 'user');
134    $task_security = getattr($filters, 'task_security');
135
136    $task = getattr($filters, 'task');
137    $round = getattr($filters, 'round');
138    $job_begin = getattr($filters, 'job_begin');
139    $job_end = getattr($filters, 'job_end');
140    $job_id = getattr($filters, 'job_id');
141    $time_begin = getattr($filters, 'time_begin');
142    $time_end = getattr($filters, 'time_end');
143    $compiler = getattr($filters, 'compiler');
144    $status = getattr($filters, 'status');
145    $score_begin = getattr($filters, 'score_begin');
146    $score_end = getattr($filters, 'score_end');
147    $eval_msg = getattr($filters, 'eval_msg');
148    $remote_ip_info = getattr($filters, 'remote_ip_info');
149
150    $wheres = array("TRUE");
151    if (!is_null($task)) {
152        $wheres[] = sprintf("`job`.`task_id` = '%s'", db_escape($task));
153    }
154    if (!is_null($user)) {
155        // In case of username filter, do a query on `ia_user` to get user_id
156        // then add `ia_job` table where clause for user_id
157        $query = sprintf("
158            SELECT `id`
159            FROM `ia_user`
160            WHERE `username` = '%s'", db_escape($user));
161        $user_id = db_fetch($query);
162        $user_id = getattr($user_id, 'id', -1);
163        $wheres[] = sprintf("`job`.`user_id` = %s", db_escape($user_id));
164    }
165    if (!is_null($round)) {
166        $wheres[] = sprintf("`job`.`round_id` = '%s'", db_escape($round));
167    }
168    if (!is_null($job_begin) && is_whole_number($job_begin)) {
169        $wheres[] = sprintf("`job`.`id` >= '%s'", db_escape($job_begin));
170    }
171    if (!is_null($job_end) && is_whole_number($job_end)) {
172        $wheres[] = sprintf("`job`.`id` <= '%s'", db_escape($job_end));
173    }
174    if (!is_null($job_id) && is_whole_number($job_id)) {
175        $wheres[] = sprintf("`job`.`id` = '%s'", db_escape($job_id));
176    }
177    if (!is_null($time_begin) && strtotime($time_begin) !== false) {
178        $time_begin = db_date_format(strtotime($time_begin));
179        $wheres[] = sprintf("`job`.`submit_time` >= '%s'", db_escape($time_begin));
180    }
181    if (!is_null($time_end) && strtotime($time_end) !== false) {
182        $time_end = db_date_format(strtotime($time_end));
183        $wheres[] = sprintf("`job`.`submit_time` <= '%s'", db_escape($time_end));
184    }
185    if (!is_null($compiler)) {
186        $wheres[] = sprintf("`job`.`compiler_id` = '%s'", db_escape($compiler));
187    }
188    if (!is_null($status)) {
189        $wheres[] = sprintf("`job`.`status` = '%s'", db_escape($status));
190    }
191    if (!is_null($eval_msg)) {
192        $wheres[] = sprintf("`job`.`eval_message` LIKE '%s%%'", db_escape($eval_msg));
193    }
194    if (!is_null($remote_ip_info)) {
195        // We allow remote_ip_info to contain % wildcards. This will make it a bit
196        // easier to search for IP classes.
197        $wheres[] = sprintf("`job`.`remote_ip_info` LIKE '%s'", db_escape($remote_ip_info));
198    }
199
200    return $wheres;
201}
202
203// Returns a where clause array based on complex filters
204// that are not related to ia_job table
205function job_get_range_wheres($filters) {
206    $user = getattr($filters, 'user');
207    $task_security = getattr($filters, 'task_security');
208
209    $task = getattr($filters, 'task');
210    $round = getattr($filters, 'round');
211    $job_begin = getattr($filters, 'job_begin');
212    $job_end = getattr($filters, 'job_end');
213    $job_id = getattr($filters, 'job_id');
214    $time_begin = getattr($filters, 'time_begin');
215    $time_end = getattr($filters, 'time_end');
216    $compiler = getattr($filters, 'compiler');
217    $status = getattr($filters, 'status');
218    $score_begin = getattr($filters, 'score_begin');
219    $score_end = getattr($filters, 'score_end');
220    $eval_msg = getattr($filters, 'eval_msg');
221
222    $wheres = array("TRUE");
223    if (!is_null($score_begin) && is_whole_number($score_begin)) {
224        $wheres[] = sprintf("(`job`.`score` >= '%s') AND (`round`.`public_eval` = 1)", db_escape($score_begin));
225    }
226    if (!is_null($score_end) && is_whole_number($score_end)) {
227        $wheres[] = sprintf("(`job`.`score` <= '%s') AND (`round`.`public_eval` = 1)", db_escape($score_end));
228    }
229    if (array_key_exists($task_security, task_get_security_types())) {
230        $wheres[] = sprintf("`task`.`security` = %s",
231            db_escape($task_security));
232    }
233
234    return $wheres;
235}
236
237// Get a range of jobs, ordered by submit time. Really awesome filterss!
238function job_get_range($filters, $start, $range) {
239    log_assert(is_whole_number($start));
240    log_assert(is_whole_number($range));
241    log_assert($start >= 0);
242    log_assert($range >= 0);
243
244    $wheres = job_get_range_wheres($filters);
245    $wheres_job = job_get_range_wheres_job($filters);
246
247    $query = "
248        SELECT
249            `job`.`id`,
250            `job`.`user_id` AS `user_id`,
251            `job`.`round_id` AS `round_id`,
252            `job`.`task_id` AS `task_id`,
253            `job`.`submit_time`,
254            `job`.`compiler_id`,
255            `job`.`status`,
256            `job`.`score`,
257            `job`.`eval_message`,
258            `job`.`eval_log`,
259            `job`.`remote_ip_info`,
260            OCTET_LENGTH(`job`.`file_contents`) AS `job_size`,
261            `user`.`username` AS `user_name`,
262            `user`.`full_name` AS `user_fullname`,
263            `task`.`page_name` AS `task_page_name`,
264            `task`.`title` AS `task_title`,
265            `task`.`security` AS `task_security`,
266            `task`.`user_id` AS `task_owner_id`,
267            `task`.`open_source` AS `task_open_source`,
268            `round`.`page_name` AS `round_page_name`,
269            `round`.`title` AS `round_title`,
270            `round`.`state` AS `round_state`,
271            `round`.`type` AS `round_type`,
272            `round`.`public_eval` AS `round_public_eval`
273        #    (CASE WHEN `status` = 'processing' THEN
274        #        (SELECT `value` FROM `ia_parameter_value` WHERE
275        #            `ia_parameter_value`.`object_id` = `task_id` AND
276        #            `ia_parameter_value`.`object_type` = 'task' AND
277        #            `ia_parameter_value`.`parameter_id` = 'tests')
278        #        ELSE NULL END) AS `total_tests`,
279        #    (CASE WHEN `status` = 'processing' THEN
280        #        (SELECT COUNT(*) FROM `ia_job_test`
281        #            WHERE `ia_job_test`.`job_id` = `job`.`id`)
282        #        ELSE NULL END) AS `done_tests`
283        FROM `ia_job` AS `job`
284        LEFT JOIN `ia_task` AS `task` ON `job`.`task_id` = `task`.`id`
285        LEFT JOIN `ia_round` AS `round` ON `job`.`round_id` = `round`.`id`
286        LEFT JOIN `ia_user` AS `user` ON `job`.`user_id` = `user`.`id`";
287
288    if (!isset($wheres[1])) {
289        // if we have no filters outside of `ia_job` table then optimize query
290        $subquery = "
291            SELECT `job`.`id` AS `ID`
292            FROM `ia_job` as `job`
293            WHERE (" . implode(") AND (", $wheres_job) . ")
294            ORDER BY `id` DESC LIMIT {$start}, {$range}";
295
296        $job_ids_fetched = db_fetch_all($subquery);
297        $job_ids = array();
298        foreach ($job_ids_fetched as $job_id) {
299            $job_ids[] = $job_id["ID"];
300        }
301        if (empty($job_ids)) {
302            return array();
303        }
304        $query .= "
305            WHERE `job`.`id` IN (" . implode(", ", array_map('db_quote', $job_ids)) . ")
306            ORDER BY `job`.`id` DESC";
307    } else {
308        // we have filters outside of `ia_job` table, we can't query in query
309        $query .= "
310            WHERE (".implode(") AND (", $wheres).") AND (".implode(") AND (", $wheres_job).")";
311        $query .= sprintf(" ORDER BY `job`.`id` DESC LIMIT %s, %s", $start, $range);
312    }
313
314    $result = db_fetch_all($query);
315
316    return $result;
317}
318
319// Counts jobs based on complex filters
320function job_get_count($filters) {
321    $query = "
322        SELECT COUNT(*) AS `cnt`
323        FROM
324            `ia_job` AS `job`";
325
326    if (getattr($filters, 'task_security')) {
327        $query .= "
328            LEFT JOIN `ia_task` AS `task` ON `job`.`task_id` = `task`.`id`";
329    }
330
331    // score_begin and score_end filters shouldn't work on rounds
332    // without public eval, so we join with ia_round
333    if (getattr($filters, 'score_begin') || getattr($filters, 'score_end')) {
334        $query .= "
335            LEFT JOIN `ia_round` AS `round` ON `job`.`round_id` = `round`.`id`";
336    }
337
338    $wheres = job_get_range_wheres($filters);
339    $wheres_job = job_get_range_wheres_job($filters);
340    $query .= "
341        WHERE (".implode(") AND (", $wheres).") AND
342              (".implode(") AND (", $wheres_job).")";
343
344    $res = db_fetch($query);
345    return $res['cnt'];
346}
347
348// Re-eval a bunch of jobs based on complex filterss
349function job_reeval($filters) {
350    $query = <<<SQL
351UPDATE `ia_job` AS `job`
352       LEFT JOIN `ia_user` AS `user` ON `job`.`user_id` = `user`.`id`
353       LEFT JOIN `ia_task` AS `task` ON `job`.`task_id` = `task`.`id`
354       LEFT JOIN `ia_round` AS `round` ON `job`.`round_id` = `round`.`id`
355SET `job`.`status` = "waiting"
356SQL;
357    $wheres = job_get_range_wheres($filters);
358    $wheres_job = job_get_range_wheres_job($filters);
359    $query .= " WHERE (".implode(") AND (", $wheres).") AND (".implode(") AND (", $wheres_job).")";
360    return db_query($query);
361}
362
363// Updates ia_job_test table
364function job_test_update($job_id, $test_number, $test_group, $exec_time, $mem_limit,
365                         $grader_exec_time, $grader_mem_limit, $points, $grader_msg) {
366    $query = sprintf("DELETE FROM ia_job_test WHERE job_id = '%s' AND test_number = '%s'",
367                     db_escape($job_id), db_escape($test_number));
368    db_query($query);
369    $query = sprintf("INSERT INTO ia_job_test
370                     (job_id, test_number, test_group, exec_time, mem_used,
371                      grader_exec_time, grader_mem_used, points, grader_message)
372                     VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
373                     db_escape($job_id), db_escape($test_number), db_escape($test_group),
374                     db_escape($exec_time), db_escape($mem_limit), db_escape($grader_exec_time),
375                     db_escape($grader_mem_limit), db_escape($points), db_escape($grader_msg));
376    return db_query($query);
377}
378
379// Returns an array of test informations for a job, ordered by test group
380function job_test_get_all($job_id) {
381    $query = sprintf("SELECT * FROM `ia_job_test`
382                      WHERE job_id = '%s' ORDER BY test_group, test_number",
383                     db_escape($job_id));
384    return db_fetch_all($query);
385}
386
387// Returns an array of public test informations for a job
388function job_test_get_public($job_id) {
389    $query = sprintf("
390        SELECT `test_count`, `public_tests` FROM `ia_task`
391        WHERE `id` = (
392            SELECT `task_id` FROM `ia_job`
393            WHERE `id` = %s
394        )", db_quote($job_id));
395
396    $task = db_fetch($query);
397    log_assert(!is_null($task));
398
399    $test_ids = task_parse_test_group($task["public_tests"], $task["test_count"]);
400    if (!count($test_ids)) {
401        return array();
402    }
403
404    $query = sprintf("
405        SELECT * FROM `ia_job_test`
406        WHERE `job_id` = %s AND `test_number` IN (%s)
407        ORDER BY `test_number`",
408        db_quote($job_id),
409        implode(", ", array_map("db_quote", array_values($test_ids))));
410    return db_fetch_all($query);
411}
Note: See TracBrowser for help on using the repository browser.