-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmakeMigrations.go
637 lines (572 loc) · 16.6 KB
/
makeMigrations.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
package main
import (
"database/sql"
"log"
)
// ****************Token table**********************
var createTokenTable = `
CREATE TABLE token (
id VARCHAR(512) NOT NULL,
username VARCHAR(32) NOT NULL,
role VARCHAR(32) NOT NULL,
issue_at DATETIME NOT NULL,
expired_at DATETIME NOT NULL,
PRIMARY KEY(id)
)
`
// ****************END of token table********************************
// ****************User login function*******************************
var loginUserFunc = `
CREATE FUNCTION login_user ( username VARCHAR(7) , user_password VARCHAR(512) )
RETURNS VARCHAR(16) DETERMINISTIC
BEGIN
DECLARE user_hashed_password VARCHAR(512);
DECLARE LOGIN_STATUS int DEFAULT 0;
DECLARE RETURN_VALUE VARCHAR(16) DEFAULT "FAIL";
SET user_hashed_password := MD5(user_password);
SELECT count(*) INTO LOGIN_STATUS
FROM student
WHERE student.student_no=username AND student.password=user_hashed_password;
IF LOGIN_STATUS=0 THEN
SELECT count(*) INTO LOGIN_STATUS
FROM professor
WHERE professor.professor_no=username AND professor.password=user_hashed_password;
IF LOGIN_STATUS > 0 THEN
SET RETURN_VALUE = "PROFESSOR";
END IF;
ELSEIF LOGIN_STATUS > 0 THEN
SET RETURN_VALUE = "STUDENT";
END IF;
RETURN RETURN_VALUE;
END;
`
// ****************End of User login function************************
// ***************Student TABLE***************
var createStudent = `
CREATE TABLE student (
national_code CHAR(10),
student_no CHAR(7),
full_name_fa VARCHAR(40) NOT NULL,
full_name_en VARCHAR(40) NOT NULL,
father_name VARCHAR(40) NOT NULL,
birth_date VARCHAR(40) NOT NULL,
mobile CHAR(11),
major VARCHAR(64) NOT NULL,
password VARCHAR(512),
email VARCHAR(64),
PRIMARY KEY (student_no),
UNIQUE(national_code)
)
`
var dropStudent = `DROP TABLE student`
// this trigger create email for user and also set default hashed password for user that starts with national_code + first_char of his first_name in capital form + first_char of last_name in lower form
var studentTriggerBeforeSave = `
CREATE TRIGGER set_student_password_email BEFORE INSERT
ON student
FOR EACH ROW
BEGIN
DECLARE full_name_en VARCHAR(40);
DECLARE _password VARCHAR(512);
SET full_name_en := REPLACE(LOWER(NEW.full_name_en), " ", "");
SET _password := CONCAT(NEW.national_code, UPPER(SUBSTRING(full_name_en, 1, 1)), LOWER(SUBSTRING(full_name_en, POSITION("-" IN full_name_en)+1, 1)));
SET NEW.email = CONCAT(SUBSTRING(full_name_en, 1, 1), ".", SUBSTRING(full_name_en, POSITION("-" IN full_name_en)+1), "@aut.ac.ir");
SET NEW.password = MD5(_password);
END
`
var dropStudentTrigger = `DROP TRIGGER set_student_password_email`
var createStudentChangePasswordFunc = `
CREATE FUNCTION change_student_password ( student_no VARCHAR(7) , student_password VARCHAR(512), student_new_password VARCHAR(512) )
RETURNS INT DETERMINISTIC
BEGIN
DECLARE user_old_password VARCHAR(512);
DECLARE user_new_password VARCHAR(512);
DECLARE AFFECTED_ROWS int DEFAULT 0;
declare ERROR_MESSAGE varchar(128);
if student_new_password REGEXP '^[0-9]+$' or student_new_password REGEXP '^[A-Za-z]+$' then
set ERROR_MESSAGE = "Password should be alphanumeric";
signal sqlstate '45000' set message_text = ERROR_MESSAGE;
end if;
if LENGTH(student_new_password) < 3 THEN
set ERROR_MESSAGE = "Password is too short";
signal sqlstate '45000' set message_text = ERROR_MESSAGE;
END IF;
if LENGTH(student_new_password) > 20 THEN
set ERROR_MESSAGE = "Password is too long";
signal sqlstate '45000' set message_text = ERROR_MESSAGE;
END IF;
SET user_old_password := MD5(student_password);
SET user_new_password := MD5(student_new_password);
UPDATE student
SET student.password = user_new_password
WHERE student.student_no = student_no AND student.password = user_old_password;
SELECT ROW_COUNT() into AFFECTED_ROWS;
RETURN AFFECTED_ROWS;
END;
`
// ***************END of student TABLE*********************
// ***************professor TABLE*******************************
var createProfessor = `
CREATE TABLE professor (
national_code CHAR(10),
professor_no CHAR(5),
full_name_fa VARCHAR(40) NOT NULL,
full_name_en VARCHAR(40) NOT NULL,
father_name VARCHAR(40) NOT NULL,
birth_date VARCHAR(40) NOT NULL,
mobile CHAR(11),
department VARCHAR(64) NOT NULL,
title ENUM("استاد", "استادیار", "دانشیار") NOT NULL,
password VARCHAR(512),
email VARCHAR(64),
PRIMARY KEY (professor_no),
UNIQUE(national_code)
)
`
var dropProfessor = `DROP TABLE professor`
var createProfessorChangePassword = `
CREATE FUNCTION change_professor_password ( professor_no CHAR(5) , professor_password VARCHAR(512), professor_new_password VARCHAR(512) )
RETURNS INT DETERMINISTIC
BEGIN
DECLARE user_old_password VARCHAR(512);
DECLARE user_new_password VARCHAR(512);
DECLARE AFFECTED_ROWS int DEFAULT 0;
SET user_old_password := MD5(professor_password);
SET user_new_password := MD5(professor_new_password);
UPDATE professor
SET professor.password = user_new_password
WHERE professor.professor_no = professor_no AND professor.password = user_old_password;
SELECT ROW_COUNT() into AFFECTED_ROWS;
RETURN AFFECTED_ROWS;
END;
`
// this trigger create email for user and also set default hashed password for user that starts with national_code + first_char of his first_name in capital form + first_char of last_name in lower form
var professorTriggerBeforeSave = `
CREATE TRIGGER set_professor_password_email BEFORE INSERT
ON professor
FOR EACH ROW
BEGIN
DECLARE full_name_en VARCHAR(40);
DECLARE _password VARCHAR(512);
SET full_name_en := REPLACE(LOWER(NEW.full_name_en), " ", "");
SET _password := CONCAT(NEW.national_code, UPPER(SUBSTRING(full_name_en, 1, 1)), LOWER(SUBSTRING(full_name_en, POSITION("-" IN full_name_en)+1, 1)));
SET NEW.email = CONCAT(SUBSTRING(full_name_en, 1, 1), ".", SUBSTRING(full_name_en, POSITION("-" IN full_name_en)+1), "@aut.ac.ir");
SET NEW.password = MD5(_password);
END
`
var dropProfessorTrigger = `DROP TRIGGER set_professor_password_email`
// **************End of professor TABLE****************
// **************course TABLE**************************
var createCourse = `
CREATE TABLE course (
course_id CHAR(8),
course_name VARCHAR(64),
professor_no CHAR(5) NOT NULL,
PRIMARY KEY(course_id),
FOREIGN KEY(professor_no) REFERENCES professor(professor_no)
)
`
var dropCourse = `DROP TABLE course`
// **************end of course TABLE**************************
var createCourseTakes = `
CREATE TABLE course_takes(
student_no CHAR(7),
course_id CHAR(8),
PRIMARY KEY(student_no, course_id),
FOREIGN KEY(course_id) REFERENCES course(course_id),
FOREIGN KEY(student_no) REFERENCES student(student_no)
)
`
var dropCourseTakes = `DROP YABLE course_takes`
// ***************Exam TABLE***********
var createExam = `
CREATE TABLE exam(
exam_id INT AUTO_INCREMENT,
exam_name VARCHAR(32),
start_date DATETIME,
end_date DATETIME,
duration INT,
course_id CHAR(8),
PRIMARY KEY (exam_id),
FOREIGN KEY (course_id) REFERENCES course(course_id)
)
`
var dropExam = `DROP TABLE exam`
var createExamFunction = `
CREATE FUNCTION create_exam (
professor_no CHAR(5),
exam_name VARCHAR(32),
start_date DATETIME,
end_date DATETIME,
duration INT,
course_id CHAR(8)
)
RETURNS VARCHAR(32) DETERMINISTIC
BEGIN
DECLARE COURSE_FOUND int DEFAULT 0;
DECLARE RETURN_VALUE VARCHAR(32) DEFAULT "FAIL";
DECLARE ERROR_MESSAGE varchar(128);
SELECT COUNT(*) INTO COURSE_FOUND
FROM course
WHERE course.course_id=course_id AND course.professor_no=professor_no;
IF COURSE_FOUND=0 THEN
set ERROR_MESSAGE = "course not found for professor";
signal sqlstate '45000' set message_text = ERROR_MESSAGE;
ELSE
INSERT INTO exam (exam_name, start_date, end_date, duration, course_id) VALUES (
exam_name,
start_date,
end_date,
duration,
course_id
);
SET RETURN_VALUE="SUCCESS";
END IF;
RETURN RETURN_VALUE;
END;
`
// ***************END of exam TABLE***********
// ***************exam question***********
var createExamQuestion = `
CREATE TABLE exam_question (
question_id INT AUTO_INCREMENT,
question_description varchar(512) NOT NULL,
first_choice VARCHAR(512) NOT NULL,
second_choice VARCHAR(512) NOT NULL,
third_choice VARCHAR(512) NOT NULL,
fourth_choice VARCHAR(512) NOT NULL,
score INT NOT NULL,
correct_answer ENUM('A', 'B', 'C', 'D'),
exam_id INT NOT NULL,
PRIMARY KEY (question_id),
FOREIGN KEY (exam_id) REFERENCES exam(exam_id)
)
`
var dropExamQuestion = `DROP TABLE exam_question`
var createExamQuestionFunction = `
CREATE FUNCTION create_exam_question (
professor_no VARCHAR(5),
question_description varchar(512),
first_choice VARCHAR(512),
second_choice VARCHAR(512),
third_choice VARCHAR(512),
fourth_choice VARCHAR(512),
score INT,
correct_answer ENUM('A', 'B', 'C', 'D'),
exam_id INT
)
RETURNS VARCHAR(32) DETERMINISTIC
BEGIN
DECLARE EXAM_FOUND int DEFAULT 0;
DECLARE RETURN_VALUE VARCHAR(32) DEFAULT "FAIL";
DECLARE ERROR_MESSAGE varchar(128);
SELECT COUNT(*) INTO EXAM_FOUND
FROM exam, course
WHERE exam.exam_id = exam_id
AND exam.course_id=course.course_id
AND course.professor_no=professor_no;
IF EXAM_FOUND=0 THEN
set ERROR_MESSAGE = "exam not found for professor";
signal sqlstate '45000' set message_text = ERROR_MESSAGE;
ELSE
INSERT INTO exam_question (question_description, first_choice, second_choice, third_choice, fourth_choice, score, correct_answer, exam_id) VALUES (
question_description,
first_choice,
second_choice,
third_choice,
fourth_choice,
score,
correct_answer,
exam_id
);
SET RETURN_VALUE="SUCCESS";
END IF;
RETURN RETURN_VALUE;
END;
`
// ***************END of exam question***********
// ***************ExamAsnwer TABLE*********************
var createExamAnswer = `
CREATE TABLE exam_answer (
question_id INT,
student_no CHAR(7),
user_answer ENUM('A', 'B', 'C', 'D') NOT NULL,
score INT DEFAULT 0,
PRIMARY KEY(question_id, student_no),
FOREIGN KEY(student_no) REFERENCES student(student_no),
FOREIGN KEY(question_id) REFERENCES exam_question(question_id)
)
`
// exam_id INT NOT NULL,
var submitExamAnswer = `
CREATE FUNCTION submit_exam_answer (
student_no VARCHAR(7),
question_id INT,
user_answer CHAR(1)
)
RETURNS VARCHAR(32) DETERMINISTIC
BEGIN
DECLARE QUESTION_FOUND int DEFAULT 0;
DECLARE RETURN_VALUE VARCHAR(32) DEFAULT "FAIL";
DECLARE ERROR_MESSAGE varchar(128);
DECLARE EXAM_TIME_IS_NOT_OVER INT DEFAULT 1;
DECLARE EXAM_TIME_HAS_BEGUN INT DEFAULT 0;
SELECT COUNT(*) INTO QUESTION_FOUND
FROM exam_question, exam, course_takes
WHERE
exam_question.question_id = question_id
AND exam_question.exam_id=exam.exam_id
AND exam.course_id=course_takes.course_id
AND course_takes.student_no=student_no;
IF QUESTION_FOUND=0 THEN
set ERROR_MESSAGE = "question not found for student";
signal sqlstate '45000' set message_text = ERROR_MESSAGE;
ELSE
SELECT COUNT(*) INTO EXAM_TIME_IS_NOT_OVER
FROM exam, exam_question
WHERE
exam_question.question_id=question_id
AND exam_question.exam_id=exam.exam_id
AND exam.end_date > NOW();
IF EXAM_TIME_IS_NOT_OVER=0 THEN
set ERROR_MESSAGE = "exam time is over dude!";
signal sqlstate '45000' set message_text = ERROR_MESSAGE;
END IF;
SELECT COUNT(*) INTO EXAM_TIME_HAS_BEGUN
FROM exam, exam_question
WHERE
exam_question.question_id=question_id
AND exam_question.exam_id=exam.exam_id
AND exam.start_date < NOW();
IF EXAM_TIME_HAS_BEGUN=0 THEN
set ERROR_MESSAGE = "exam has not begin yet dude!";
signal sqlstate '45000' set message_text = ERROR_MESSAGE;
END IF;
INSERT INTO exam_answer (question_id, student_no, user_answer) VALUES (
question_id,
student_no,
user_answer
);
SET RETURN_VALUE="SUCCESS";
END IF;
RETURN RETURN_VALUE;
END;
`
var dropSubmitExamAnswer = `DROP FUNCTION submit_exam_answer`
//*****************************8
var getStudentExamScore = `
CREATE FUNCTION get_student_exam_score (student_no VARCHAR(7), exam_id INT)
RETURNS INT DETERMINISTIC
BEGIN
DECLARE score int DEFAULT 0;
DECLARE EXAM_IS_OVER INT DEFAULT 0;
DECLARE ERROR_MESSAGE VARCHAR(64);
SELECT COUNT(*) INTO EXAM_IS_OVER
FROM exam
WHERE
exam.exam_id=exam_id
AND exam.end_date < NOW();
IF EXAM_IS_OVER=0 THEN
set ERROR_MESSAGE = "Exam is not over yet!";
signal sqlstate '45000' set message_text = ERROR_MESSAGE;
END IF;
SELECT COALESCE(SUM(
CASE
WHEN exam_answer.user_answer = exam_question.correct_answer
THEN exam_question.score
ELSE 0
END
), -1) INTO score
FROM exam_answer, exam_question, exam, course_takes
WHERE
exam_answer.question_id=exam_question.question_id
AND exam_question.exam_id=exam_id
AND exam_question.exam_id=exam.exam_id
AND exam.course_id=course_takes.course_id
AND course_takes.student_no=student_no;
RETURN score;
END;
`
var dropGetStudentExamScoreFunc = `DROP FUNCTION get_student_exam_score`
// ***************End of Exam TABLE**************
// var createqUESTIONAnswer = `
// CREATE TABLE exam_answer(
// student_id CHAR(7),
// short_question_id int,
// answer VARCHAR(200),
// student_grade int DEFAULT 0,
// PRIMARY KEY(student_id, short_question_id),
// FOREIGN KEY(student_id) REFERENCES student(student_id),
// FOREIGN KEY(short_question_id) REFERENCES short_question(question_id)
// )
// `
// var dropShortAnswer = `DROP TABLE short_answer`
// var createHomework = `
// CREATE TABLE hw(
// id int AUTO_INCREMENT,
// hw_number int UNSIGNED,
// -- section_id
// course_code int NOT NULL,
// group_number VARCHAR(4) NOT NULL,
// year CHAR(4) NOT NULL,
// semester ENUM('FALL', 'SPRING'),
// --
// description VARCHAR(200) NOT NULL,
// PRIMARY KEY(id),
// FOREIGN KEY(course_code, group_number, year, semester) REFERENCES section(course_code, group_number, year, semester)
// )
// `
// var dropHomework = `DROP TABLE hw`
// var createHomeworkParticipation = `
// CREATE TABLE hw_participation(
// student_id CHAR(7),
// hw_id int,
// date DATE not null,
// grade int UNSIGNED DEFAULT 0,
// file VARCHAR(400) NOT NULL,
// PRIMARY KEY(student_id, hw_id),
// FOREIGN KEY(student_id) REFERENCES student(student_id),
// FOREIGN KEY(hw_id) REFERENCES hw(id)
// )
// `
// var dropHomeworkParticipation = `DROP TABLE hw_participation`
var execs = []struct {
stmt string
shouldFail bool
}{
{
stmt: createStudent,
shouldFail: false,
},
{
stmt: studentTriggerBeforeSave,
shouldFail: false,
},
{
stmt: createStudentChangePasswordFunc,
shouldFail: false,
},
{
stmt: loginUserFunc,
shouldFail: false,
},
{
stmt: createTokenTable,
shouldFail: false,
},
{
stmt: createProfessor,
shouldFail: false,
},
{
stmt: professorTriggerBeforeSave,
shouldFail: false,
},
{
stmt: createCourse,
shouldFail: false,
},
{
stmt: createCourseTakes,
shouldFail: false,
},
{
stmt: createExam,
shouldFail: false,
},
{
stmt: createExamQuestion,
shouldFail: false,
},
{
stmt: createExamQuestionFunction,
shouldFail: false,
},
{
stmt: createExamAnswer,
shouldFail: false,
},
{
stmt: dropSubmitExamAnswer,
shouldFail: false,
},
{
stmt: submitExamAnswer,
shouldFail: false,
},
{
stmt: createExamFunction,
shouldFail: false,
},
{
stmt: dropGetStudentExamScoreFunc,
shouldFail: false,
},
{
stmt: getStudentExamScore,
shouldFail: false,
},
// {
// stmt: dropStudent,
// shouldFail: false,
// },
// {
// stmt: dropProfessor,
// shouldFail: false,
// },
// {
// stmt: dropCourse,
// shouldFail: false,
// },
// {
// stmt: dropSection,
// shouldFail: false,
// },
// {
// stmt: dropCourseTakes,
// shouldFail: false,
// },
// {
// stmt: dropExam,
// shouldFail: false,
// },
// {
// stmt: dropTestQuestion,
// shouldFail: false,
// },
// {
// stmt: dropShortQuestion,
// shouldFail: false,
// },
// {
// stmt: dropTestAnswer,
// shouldFail: false,
// },
// {
// stmt: dropShortAnswer,
// shouldFail: false,
// },
// {
// stmt: dropHomework,
// shouldFail: false,
// },
// {
// stmt: dropHomeworkParticipation,
// shouldFail: false,
// },
}
func MakeMigrations(db *sql.DB) {
for _, exec := range execs {
_, err := db.Exec(exec.stmt)
hasFailed := err != nil
if exec.shouldFail != hasFailed {
expected := "succeed"
if exec.shouldFail {
expected = "fail"
}
log.Printf("'%s' should have %sed but did not: %s", exec.stmt, expected, err)
} else if exec.shouldFail {
log.Printf("'%s' failed as expected: %s", exec.stmt, err)
}
}
log.Println("finish!")
}