-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsql-validator.rkt
More file actions
174 lines (144 loc) · 6.8 KB
/
Copy pathsql-validator.rkt
File metadata and controls
174 lines (144 loc) · 6.8 KB
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
#lang racket
;;
;; Author: beefy24 (https://github.com/beefy24), 2024.
;;
;; ╔══════════════════════════════════════════════════════════════════════════════╗
;; ║ PROGRAM DESCRIPTION: ║
;; ║ • This is a simplified SQL SELECT query string validator. ║
;; ║ • Use it by calling the `validate` function followed by a string input. ║
;; ║ • Example: `(validate "SELECT * FROM table ;")` ║
;; ║ • All symbols are separated by a blank space, columns by a comma. ║
;; ║ • Column and table names can be surrounded by [] and "", respectively. ║
;; ║ • More examples are shown at the end of this file. ║
;; ╚══════════════════════════════════════════════════════════════════════════════╝
;;
;;String library
(require racket/string)
;;A function alias
(define eq? equal?)
;;The list of all possible tokens/symbols.
(define tokens '("SELECT" "ALL" "DISTINCT" "DISTINCTROW" "*" "," "FROM" ";" "" null #\newline))
;; PARSE TABLE (REGULAR GRAMMAR):
;; ╔══════════╦═══╦════════╦══════════════╦════════╦════════════╦═══╦══════╦═══════════╦═════╗
;; ║ VARIABLE ║ ║ SELECT ║ ALL|DISTINCT ║ * FROM ║ ColumnName ║ , ║ FROM ║ TableName ║ ;ε ║
;; ╠══════════╬═══╬════════╬══════════════╬════════╬════════════╬═══╬══════╬═══════════╬═════╣
;; ║ START-> ║ ║ A ║ ║ ║ ║ ║ ║ ║ ║
;; ║ A -> ║ B ║ ║ B ║ ║ ║ ║ ║ ║ ║
;; ║ B -> ║ C ║ ║ ║ E ║ ║ ║ ║ ║ ║
;; ║ C -> ║ ║ ║ ║ ║ D ║ ║ ║ ║ ║
;; ║ D -> ║ ║ ║ ║ ║ ║ C ║ E ║ ║ ║
;; ║ E -> ║ ║ ║ ║ ║ ║ ║ ║ F ║ ║
;; ║ F -> ║ ║ ║ ║ ║ ║ ║ ║ ║ END ║
;; ╚══════════╩═══╩════════╩══════════════╩════════╩════════════╩═══╩══════╩═══════════╩═════╝
;; Note: The 2nd unnamed column is a default rule, which is applied when no other rules match the input symbol.
;;The main function which checks the input type and if it's not empty.
(define (validate input)
(displayln
(string-append input
(cond
( (not(string? input)) " Not a String!" )
( (not(non-empty-string? (string-normalize-spaces input))) " No Input" )
;;Apends the input with an EOL character `#\newline` and calls the first rule S.
( #t (ruleS (append (string-split (string-normalize-spaces input)) '(#\newline))) )
)
)
)
)
;;Compares the input and the expected token (strings).
(define (matchToken input token)
(cond
( (eq? (car input) token) #t )
(#t #f)
)
)
;;Validates a table/column name with the regular expression.
(define (validateName input)
(cond
( (regexp-match #rx"^[_a-zA-Z][_a-zA-Z0-9]*$" (car input)) #t )
( (regexp-match #rx"^\".+\"$" (car input)) #t )
( (regexp-match #rx"^\\[.+\\]$" (car input)) #t )
( #t #f )
)
)
;;Displays an error message.
(define (error message)
message
;(begin
;(write message)
;#f
;)
)
;;Rule: S -> select A
(define (ruleS input)
(cond
( (matchToken input "SELECT") (ruleA (cdr input)) ) ;;Expects the first token SELECT and calls the following rule.
( #t (error " SELECT Expected!") )
)
)
;;Rule: A -> all B | distinct B | distinctrow B | B
;;These tokens are optional. This rule is skipped if no matching token is provided.
(define (ruleA input)
(cond
( (matchToken input "ALL") (ruleB (cdr input)) )
( (matchToken input "DISTINCT") (ruleB (cdr input)) )
( (matchToken input "DISTINCTROW") (ruleB (cdr input)) )
( #t (ruleB input) )
)
)
;;Rule: B -> * form E | C
(define (ruleB input)
(cond
( (matchToken input "*") (cond ( (matchToken (cdr input) "FROM") (ruleE (cddr input)) ) ( #t (error " FROM Expected!") ) ) )
( #t (ruleC input) )
)
)
;;Rule: C -> column D
;;Expects a column and validates its name.
(define (ruleC input)
(cond
( (not (member (car input) tokens)) (cond ( (validateName input) (ruleD (cdr input)) ) ( #t (error " Invalid Column Name!") ) ) )
( #t (error " Column Reference Expected!") )
)
)
;;Rule: D -> , C | from E
(define (ruleD input)
(cond
( (matchToken input ",") (ruleC (cdr input)) )
( (matchToken input "FROM") (ruleE (cdr input)) )
( #t (error " FROM Expected!") )
)
)
;;Rule: E -> table F
(define (ruleE input)
(cond
( (not (member (car input) tokens)) (cond ( (validateName input) (ruleF (cdr input)) ) ( #t (error " Invalid Table Name!")) ) )
( #t (error " Table Reference Expected!") )
)
)
;;Rule: F -> ;ε
;;The final rule which expects the SQL query tereminator followed by a #\newline character.
(define (ruleF input)
(cond
( (matchToken input ";") (cond ( (matchToken (cdr input) #\newline) " Valid Query" ) ( #t (error " EOF Expected!") ) ) )
( #t (error " Terminator Expected!") )
)
)
;;╔══════════════════════╗
;;║ EXAMPLE CALLS/TESTS: ║
;;╚══════════════════════╝
;;Valid inputs:
(displayln "Valid inputs:")
(validate "SELECT * FROM table ;")
(validate "SELECT column FROM table ;")
(validate "SELECT col1 , col2 FROM table ;")
(validate "SELECT DISTINCT col1 , col2 FROM table ;")
(validate "SELECT [col1] , [col2] FROM \"table\" ;")
(displayln "")
;;Invalid inputs:
(displayln "Invalid inputs:")
(validate "SELEC * FROM table ;")
(validate "SELECT FROM table ;")
(validate "SELECT col1 , 2 FROM table ;")
(validate "SELECT * FOR table ;")
(validate "SELECT * FROM table")
(validate "SELECT * FROM table ; ;")