Browse Source

Initial commit

Jose Ortiz 8 years ago
commit
4deada82ae
8 changed files with 698 additions and 0 deletions
  1. 54
    0
      InjectionParserTest.py
  2. 317
    0
      README.md
  3. 75
    0
      SQLInjection.py
  4. 125
    0
      SQLLexer.py
  5. 127
    0
      SQLParser.py
  6. BIN
      images/tree1.jpg
  7. BIN
      images/tree2.jpg
  8. BIN
      images/tree3.jpg

+ 54
- 0
InjectionParserTest.py View File

@@ -0,0 +1,54 @@
1
+from SQLInjection import *
2
+
3
+if __name__ == '__main__':
4
+
5
+	sqlI = SQLInjection()
6
+
7
+	# Test 1
8
+	sample = """select cat from dog where casa=1 ;"""
9
+	u_input =  """select cat from dog where casa=2 ;"""
10
+
11
+
12
+	print "Test 1"
13
+	print "Sample:  ", sample
14
+	print "User In: ", u_input
15
+	print "Is Valid?: ", sqlI.validateParser(sample, u_input)
16
+	print
17
+
18
+	# Test 2
19
+
20
+	u_input = """select cat from dog where casa=1 and cat="miau" ;"""
21
+	print "Test 1"
22
+	print "Sample:  ", sample
23
+	print "User In: ", u_input
24
+	print "Is Valid?: ", sqlI.validateParser(sample, u_input)
25
+	print
26
+
27
+	# Interactive Example with user input
28
+
29
+	print "Follow the instruction and then try to inject SQL."
30
+
31
+	while True:
32
+		try: 
33
+			s = raw_input("Input a number> ")
34
+		except EOFError:
35
+			break
36
+
37
+		u_input = """select cat from dog where casa=%s ;""" %s
38
+		print "User query: %s" % u_input
39
+
40
+		try:
41
+			print "Is Valid?: ", sqlI.validateParser(sample, u_input)
42
+			s_ast, u_ast = sqlI.getLastAsts()
43
+			print "Sample AST: "
44
+			sqlI.print_ast(s_ast)
45
+			print
46
+			print "User   AST: "
47
+			sqlI.print_ast(u_ast)
48
+			print 
49
+		except:
50
+			print "False"
51
+
52
+
53
+
54
+

+ 317
- 0
README.md View File

@@ -0,0 +1,317 @@
1
+[English](#markdown-header-grammars-sql-injection) | [Español](#markdown-header-gramaticas-inyeccion-de-sql)
2
+
3
+# Gramáticas - Inyección de SQL
4
+##Objetivos
5
+
6
+1. Practicar la implementación de gramáticas libres de contexto para construir un analizador de sintaxis de una versión simple de una pregunta de selección de SQL.
7
+ 
8
+## Pre-Modulo:
9
+
10
+### Bajar PLY
11
+Obten PLY desde: http://www.dabeaz.com/ply/
12
+
13
+###Instalar PLY
14
+
15
+Descomprime el archivo:
16
+
17
+```
18
+tar xzvf ply-X.X.tar.gz
19
+```
20
+
21
+Entra al directorio creado:
22
+
23
+```
24
+cd ply-X.X
25
+```
26
+
27
+Construye los modulos de PLY:
28
+
29
+```
30
+python setup.py build
31
+```
32
+
33
+Instala PLY en el sistema como administrador:
34
+```
35
+sudo python setup.py install
36
+```
37
+
38
+### Pre-Modulo
39
+
40
+Antes de trabajar en este módulo el estudiante debe
41
+
42
+1. Haber repasado sobre gramáticas libre de contexto.
43
+3. Haber leido sobre el constructor de analizadores sintáctico yacc.
44
+
45
+## Prevenir Inyección de SQL usando un analizador sintáctico
46
+
47
+La validación de entradas es escencial en el desarrollo de aplicaciones seguras ya que es la primera línea de defensa de todas las aplicaciones.  La gran mayoría de las vulnerabilidades en aplicaciones es debido a pobre validación de las entradas de las aplicaciones. Ejemplo de vulnerabilidades explotadas por pobre validación de entradas lo son:
48
+
49
+1. Desbordamiento del Búfer 
50
+2. Cross Site Scripting
51
+3. Inyección de SQL, XML u otros lenguajes
52
+4. Recorrido de directorios
53
+5. Acceso a archivos privados
54
+
55
+Los atacantes utilizan errores en la validación de entradas para acceder a información privilegiada, para acceder a los sistemas, o para causar negación de servicios.
56
+
57
+Utilizando un analizador sintáctico se puede comparar el árbol sintáctico generado por una pregunta de SQL válida con el tipo de entradas esperadas versus el árbol sintáctico generado por una pregunta de SQL con entradas de usuario y determinar por la diferencia de los árboles si la entrada del usuario es válida.  Si el usuario no entrase el tipo de dato válido o tratara de inyectar SQL en la entrada el árbol sintáctico cambiaría.  Otra ventaja de utilizar un analisador sintáctico es que este te puede avisar si la pregunta que se construyó es sintácticamente válida y por ende, no va a causar un error con el servidor de SQL y quizas hasta romper la aplicación.
58
+
59
+Un ejemplo de una pregunta de SQL sencilla es:
60
+
61
+```
62
+select * from infoprivada where contrasena="valor aleatorio desconocido" ;
63
+```
64
+
65
+Esta pregunta de SQL revelaría la información "privada" de un campo de una tabla si se provee la contraseña correcta.  En la aplicación donde se utiliza esta pregunta la entrada del usuario consiste de insertar la contraseña.  Un posible ataque que revelaría la información privada de toda la tabla sería que usuario insertara como valor la cadena de caracteres `1" or 1=1` que generaría la pregunta: 
66
+
67
+```
68
+select * from infoprivada where contrasena="1" or 1=1;
69
+```
70
+
71
+En cuyo caso por la lógica de la pregunta la expresión del WHERE es siempre cierta, y por lo tanto revelaría toda la información de la tabla. Para aprender más sobre Inyección de SQL vaya a la referencia[1].
72
+
73
+Para propósito de este módulo, el analyzador sintáctico que se va a construir sirve para obtener los árboles sintácticos de una versión simplificada de la pregunta de selección de SQL cuya regla sintáctica inicial es la que sigue:
74
+
75
+select : SELECT columns FROM tables WHERE where_expression SEMI
76
+
77
+los terminales de la gramática son:
78
+
79
+SELECT, FROM, WHERE, AND, OR, NUMBER, ID, LITERAL, LPAREN `(`, RPAREN `)`, EQ, NEQ, GT, LT, GTE, LTE, SEMI, COMMA, WILD `*`, COMMENT
80
+
81
+y no permite uniones o expresiones compuestas de preguntas recursivas de SQL.
82
+
83
+Por ejemplo:
84
+
85
+Para la pregunta de SQL `select * from table where field=%s`, donde %s sería la entrada del usuario; la posible entrada que requiere la aplicación donde se está utilizando puede ser un número insertado por el usuario. 
86
+
87
+Como la entrada esperada es un número, una pregunta que se conoce que va a ser válida es `select * from table where field=1`, la cual generaría el árbol sintáctico:
88
+
89
+![](images/tree1.jpg)
90
+
91
+Si un usuario entrara cualquier otro número lo cual es válido, también se generarían el mismo árbol sintáctico ya que la sintaxis no cambia.
92
+
93
+Pero si el usuario en ves de entrar un número ingresara la entrada `1 or 1=1` para que la pregunta siempre sea cierta `select * from table where field=1 or 1=1` el árbol sintáctico que se generaría es:
94
+
95
+![](images/tree2.jpg)
96
+
97
+que como se puede observar es diferente al árbol válido por que la sintasis cambía.
98
+
99
+Otro ejemplo de una entrada inválida sería poner algo diferente a un número como una cadena de caracteres como myfield `select * from table where field=myfield` el árbol sintáctico que se generaría es:
100
+
101
+![](images/tree3.jpg)
102
+
103
+que como se puede observar el árbol tiene la misma estructura, pero el tipo de uno de los nodos es distinto.
104
+
105
+**Descargo de responsabilidad** Esto es un módulo cuyo proposito es enseñar gramáticas libres de contexto y construcción de analizadores sintácticos utilizando una aplicación de seguridad.
106
+
107
+## Instrucciones generales
108
+
109
+En este módulo utilizaremos la librería de python PLY para construir un analizador sintáctico de preguntas de selección de SQL simples.  El estudiante 
110
+completará la gramática del analizador sintáctico y luego seguirá instrucciones para aprender como el analisis sintáctico sirve para prevenir inyecciones de SQL.
111
+
112
+El archivo que el estudiante modificará para llevar a cabo este módulo es:
113
+`SQLParser.py`
114
+
115
+
116
+## Ejercicio 1: Completar gramática del analizador sintáctico.
117
+
118
+Edite el archivo `SQLParser.py` para completar la gramática del analizador sintáctico en las funciones que comienzan con p, como la función inicial `p_select(self, p)`.
119
+
120
+1. La gramática para id\_list es un ID o un ID concadenado a un id\_list separado por comas.
121
+2. La gramática para simple_expression es:
122
+    1. a factor or
123
+    2. a factor less than another factor or
124
+    3. a factor greather than another factor,
125
+    4. y así sucesivamente...
126
+
127
+## Ejercicio 2: Correr el analizador sintáctico para contestar preguntas
128
+
129
+Corra el analizador sintáctico:
130
+
131
+```
132
+python  InjectionParserTest.py
133
+```
134
+Van a salir desplegadas dos pruebas donde se comparan una pregunta válida de ejemplo y otra donde las entradas del WHERE es distinta.
135
+
136
+1. Note que para la prueba 1, el resultado es válido.  Anote por qué?
137
+2. Note que para la prueba 2, el resultado es inválido.  Anote por qué?
138
+3. Inserte como entrada un número entero cualquiera. Es válido?  Por qué? (Compare los niveles de los árboles)
139
+4. Inserte como entrada `1 or 1=1`.  Es válido? Por qué? 
140
+5. Inserte como entrada `"ccom" or casa=4`.  Es válido? Por qué?
141
+
142
+## Ejercicio 3: Un analizador sintáctico para preguntas de SQL para insertar
143
+
144
+Modifica el analizador sintáctico de el archivo `SQLParser.py` para también analizar la sintaxis de preguntas de SQL simples como las siguientes: 
145
+
146
+insert into table (col1, col2, col3) value (id, 3, "literal") ;
147
+insert into table (col1, col2, col3) values (id, 3,"literal"), (id, 4, "otherliteral") ;
148
+
149
+**Nota** Crea una copia de el archivo `SQLParser.py` del Ejercicio 1 porque necesitaras entregarlo al instructor con otra copia del `SQLParser.py` para este ejercicio. 
150
+
151
+## Entregas
152
+
153
+Entregue al instructor una copia del archivo `SQLParser.py` del Ejercicio 1 y el Ejercicio 3, y las contestaciones a las preguntas del Ejercicio 2.
154
+
155
+## References:
156
+[1] https://en.wikipedia.org/wiki/SQL_injection
157
+
158
+--- 
159
+
160
+[English](#markdown-header-grammars-sql-injection) | [Español](#markdown-header-gramaticas-inyeccion-de-sql)
161
+
162
+# Grammars - SQL Injection
163
+
164
+##Objectives
165
+
166
+1. Practice the implementation of context free grammars to construct a syntax analyzer of a simple version of a SQL select query.
167
+ 
168
+## Pre-Module:
169
+
170
+### Download PLY
171
+Get PLY from: http://www.dabeaz.com/ply/
172
+
173
+###Install PLY
174
+
175
+Uncompress the file:
176
+
177
+```
178
+tar xzvf ply-X.X.tar.gz
179
+```
180
+
181
+Enter the directory created:
182
+
183
+```
184
+cd ply-X.X
185
+```
186
+
187
+Construct the PLY modules:
188
+
189
+```
190
+python setup.py build
191
+```
192
+
193
+Install PLY in the system as an administrator:
194
+
195
+```
196
+sudo python setup.py install
197
+```
198
+
199
+### Pre-Module
200
+
201
+Before working in this module the student must
202
+
203
+1. Have reviewed about context free grammars.
204
+3. Have read about the constructor of syntax analyzers yacc.
205
+
206
+## Prevention SQL injection using a syntax analyzer
207
+
208
+Input validation is esential in the development of secure applications because it is the first line of defense of every application.  The vast majority of vulnerabilities in applications is due to poor input validation of the applications.  Example of vulnerabilities explited by poor input validation are:
209
+
210
+1. Buffer overflows
211
+2. Cross Site Scripting
212
+3. SQL, XML or other languages injection
213
+4. Directory Traversal
214
+5. Access to private files
215
+
216
+The attackers use errors in input validation to gain access to priviledged information, to gain access to systems, or to cause denial of services.
217
+
218
+Using a syntax analyzer we can compare the syntax tree generated by a valid SQL query with expected inputs types versus the syntax tree generated by a SQL query with user input and determine with the difference of the trees if the user input is valid. If the user input is not of a valid type or if the user tries to inject SQL the syntax tree will change.  Another advantage of using a syntax analyzer is that it can warn if the constructed query is syntactically valid, and thus, it would not cause an error with the SQL server and perhaps even break the application.
219
+
220
+An example of a simple SQL query is:
221
+
222
+```
223
+select * from infoprivada where contrasena="valor aleatorio desconocido" ;
224
+```
225
+
226
+This SQL query would reveal "private" information of a field of the a table if the right password is provided.  In the aplication where this query would be used the user input would consist of inserting the password.  A possible atack that would reveal the private information of all the table would be that the user inserts as a value the string `1" or 1=1` that would generate the query:
227
+
228
+```
229
+select * from infoprivada where contrasena="1" or 1=1;
230
+```
231
+
232
+In which case because of the logic of the query WHERE expression is always true, and therefore it would reveal all the information in the table.  To learn more about SQL injection go to reference [1].
233
+
234
+For the purpose of this module, the syntax analyzer that will be constructed will serve to obtain the syntax trees of a simplified version of the select SQL query which init syntax rules is:
235
+
236
+select : SELECT columns FROM tables WHERE where_expression SEMI
237
+
238
+the grammar terminals are:
239
+
240
+SELECT, FROM, WHERE, AND, OR, NUMBER, ID, LITERAL, LPAREN `(`, RPAREN `)`, EQ, NEQ, GT, LT, GTE, LTE, SEMI, COMMA, WILD `*`, COMMENT
241
+
242
+and does not permits UNIONs, or expressions composed of recursive SQL queries.
243
+
244
+For example:
245
+
246
+For the SQL query `select * from table where field=%s`, where %s would be the user input; the possible input requiered by the application where the query is being used could be a number inserted by the user.
247
+
248
+Because the expected input is a number, a query known to be valid is `select * from table where field=1`, which would generate the syntax tree:
249
+
250
+![](images/tree1.jpg)
251
+
252
+If the user enters any other number, which is valid, also the same syntax tree would be generated because the syntax tree does not changes.
253
+
254
+But if the user instead of inserting a number inserts as input `1 or 1=1` to make the query always true `select * from table where field=1 or 1=1` the syntax tree that would be generated is:
255
+
256
+![](images/tree2.jpg)
257
+
258
+that as can be observed is different to the valid tree because the syntax changes.
259
+
260
+Another example of an invalid input would be to insert something different to a number such a string like myfield `select * from table where field=myfield` where the generated syntax tree is:
261
+
262
+![](images/tree3.jpg)
263
+
264
+that as can be observed it is different to the valid syntax tree because the syntax changes
265
+
266
+**Disclaimer** This is a module which purpose is to teach context free grammars and to construct syntax analyzers using security as an application.
267
+
268
+## General Instructions
269
+
270
+In this module we will use the python library PLY to construct a syntax analyzer of simple selection SQL queries.  The student will complete the grammar of the syntax analizer and then will follow instructions to learn how the syntax analyisis serves to prevent SQL injections.
271
+
272
+The file that the student will modify to accomplish this module is: `SQLParser.py`
273
+
274
+
275
+## Exercise 1: Complete the grammar of the syntax analyzer
276
+
277
+Edit the file  `SQLParser.py` to complete the grammar of the syntax analyzer in the functions that start with `p`, like the initial function `p_select(self, p)`.
278
+
279
+1. The grammar for id\_list is an ID or an ID concatenated to an id\_list separated by commas.
280
+2. The grammar for simple_expression is:
281
+    1. a factor or
282
+    2. a factor less than another factor or
283
+    3. a factor greather than another factor,
284
+    4. and so on...
285
+
286
+## Exercise 2: Run the syntax analyzer and answer the questions
287
+
288
+Run the syntax analyzer:
289
+
290
+```
291
+python  InjectionLexTest.py
292
+```
293
+The comparison between two tests, one with a sample valid query and other with a different WHERE expressions input is displayed.
294
+
295
+1. Note that for test 1, the result is valid.  Why?
296
+2. Note that for test 2, the result is invalid. Why?
297
+3. Insert as input any integer number.  Is valid? Why? (Compare the levels of the syntax tree)
298
+4. Insert as input `1 or 1=1`. Is valid? Why?
299
+5. Insert as input `"ccom" or casa=4`. Is valid? Why?
300
+``
301
+
302
+## Exercise 3: A syntax analyzer for insert queries
303
+
304
+Modify the syntax analyzer of file `SQLParser.py` to also analyze the syntax of simple insert SQL queries like the following:
305
+
306
+insert into table (col1, col2, col3) value (id, 3, "literal") ;
307
+insert into table (col1, col2, col3) values (id, 3,"literal"), (id, 4, "otherliteral") ;
308
+
309
+**Note** make a copy of the file `SQLParser.py` from Exercise 1 because you need to give it to the instructor with another `SQLParser.py` for this exercise. 
310
+
311
+## Deliverables
312
+
313
+Give the instructor a copy of the file `SQLParser.py` from Exercise 1, and Exercise 3; and the answers to the questions from Exercise 2.
314
+
315
+## References:
316
+[1] https://en.wikipedia.org/wiki/SQL_injection
317
+

+ 75
- 0
SQLInjection.py View File

@@ -0,0 +1,75 @@
1
+
2
+from SQLLexer import *
3
+from SQLParser import *
4
+
5
+
6
+class SQLInjection():
7
+
8
+	def __init__(self):
9
+			self.lexer = SQLLexer()
10
+			self.lexer.build()
11
+
12
+			self.parser = SQLParser()
13
+
14
+			self.u_tok_counter = None
15
+			self.s_tok_counter = None
16
+
17
+			self.u_ast = None
18
+			self.s_ast = None
19
+
20
+
21
+	def validateLex(self, sample_sql, user_sql):
22
+
23
+		self.s_tok_counter = self.lexer.getTokensHash()
24
+		self.u_tok_counter = self.lexer.getTokensHash()
25
+
26
+		for tok in self.lexer.tokenize(sample_sql):
27
+	 		self.s_tok_counter[tok.type] += 1
28
+
29
+	 	for tok in self.lexer.tokenize(user_sql):
30
+	 		self.u_tok_counter[tok.type] += 1
31
+
32
+	 	return self.s_tok_counter == self.u_tok_counter
33
+
34
+	def getLastTokCounters(self):
35
+		return self.s_tok_counter, self.u_tok_counter
36
+
37
+	def validateParser(self, sample_sql, user_sql):
38
+	
39
+		self.s_ast = self.parser.parse(sample_sql)
40
+		self.u_ast = self.parser.parse(user_sql)
41
+	
42
+		return self.s_ast == self.u_ast
43
+
44
+	def getLastAsts(self):
45
+		return self.s_ast, self.u_ast
46
+
47
+	def print_ast(self, ast):
48
+		Q = ast
49
+		while len(Q) > 0:
50
+			NQ = []
51
+			for node in Q:
52
+				if type(node) == tuple:
53
+					print node[0],  
54
+					for i in range(1, len(node)):
55
+						NQ.append(node[i])
56
+				else:
57
+					print node,
58
+			Q = NQ
59
+			print
60
+
61
+if __name__ == '__main__':
62
+
63
+	sqlI = SQLInjection()
64
+
65
+	# Test 1
66
+	print sqlI.validateLex("""select cat from dog where casa=1 ;""", """select cat from dog where casa=1 ;""")
67
+
68
+	# Test 2
69
+	print sqlI.validateLex("""select cat from dog where casa=1 ;""", """select cat from dog where casa=1 and cat="miau" ;""")
70
+
71
+	# Test 3
72
+	print sqlI.validateParser("""select cat from dog where casa=1 ;""", """select cat from dog where casa=1 ;""")
73
+	
74
+	# Test 2
75
+	print sqlI.validateParser("""select cat from dog where casa=1 ;""", """select cat from dog where casa=1 and cat="miau" ;""")

+ 125
- 0
SQLLexer.py View File

@@ -0,0 +1,125 @@
1
+import ply.lex as lex
2
+from ply.lex import TOKEN
3
+
4
+class SQLLexer(object):
5
+	# List of token names.  
6
+
7
+	reserved = {
8
+	
9
+		'SELECT' : 'SELECT',
10
+		'select' : 'SELECT',
11
+		'FROM' : 'FROM', 
12
+		'from' : 'FROM', 
13
+		'WHERE' : 'WHERE',
14
+		'where' : 'WHERE',
15
+		'AND' : 'AND',
16
+		'and' : 'AND',
17
+		'or' : 'OR',
18
+		'OR' : 'OR'
19
+	}
20
+
21
+	tokens = [
22
+			'COMMENT',
23
+			'NUMBER', 'ID', 'LITERAL',
24
+			'LPAREN', 'RPAREN', 
25
+			'EQ', 'NEQ', 'GT', 'LT', 'LTE', 'GTE', 'SEMI', 'COMMA', 'WILD'] + list(set(list(reserved.values())))
26
+
27
+
28
+	# REGULAR EXPRESSION RULES FOR TOKENS
29
+
30
+	number = r'\d+'
31
+	identifier = r'[a-zA-Z_][a-zA-Z0-9_]*'
32
+	literal = r'"([^"\\]|\\.)*"'    #r'\"[a-zA-Z0-9_\-=<>]*\"'
33
+	comment = r'--.*'
34
+
35
+	t_LPAREN = r'\('
36
+	t_RPAREN = r'\)'
37
+	t_LTE = r'<='
38
+	t_GTE = r'>='
39
+	t_LT = r'<'
40
+	t_GT = r'>'
41
+	t_EQ = r'='
42
+	t_NEQ = r'!='
43
+	t_SEMI = r';'
44
+	t_COMMA =r','
45
+	t_WILD = r'\*'
46
+
47
+	# Ignore space characters
48
+	t_ignore = ' \t\n'
49
+
50
+	# Actions nexts
51
+
52
+	# Only integer numbers for this example
53
+
54
+	@TOKEN(number)
55
+	def t_NUMBER(self, t):
56
+		t.value = int(t.value)
57
+		return t 
58
+
59
+	# Extract literals content
60
+
61
+	@TOKEN(literal)
62
+	def t_LITERAL(self, t):
63
+		if t.value == "\"\"":
64
+			t.value = ""
65
+		else:
66
+			t.value = t.value[1:len(t.value)-1]
67
+		return t
68
+
69
+	# Identifiers
70
+	@TOKEN(identifier)
71
+	def t_ID(self, t):
72
+		t.type = self.reserved.get(t.value, 'ID')
73
+		return t
74
+
75
+	# Ignore comments
76
+	@TOKEN(comment)
77
+	def t_COMMENT(self, t):
78
+		pass
79
+
80
+	def t_error(self, t):
81
+		print("Illegal character '%s'" % t.value[0])
82
+		raise
83
+		#t.lexer.skip(1)
84
+
85
+
86
+	#Build the lexer
87
+	def build(self, **kwargs):
88
+		self.lexer = lex.lex(module=self, **kwargs)
89
+
90
+
91
+	def input(self, data):
92
+		self.lexer.input(data)
93
+
94
+	def token(self):
95
+		return self.lexer.token()
96
+
97
+	def tokenize(self, data):
98
+		tokens = []
99
+		self.lexer.input(data)
100
+		while True:
101
+			tok = self.lexer.token()
102
+			if not tok:
103
+				break
104
+			tokens.append(tok)
105
+		return tokens
106
+
107
+	def getTokensHash(self):
108
+		return dict((k,0) for k in self.tokens)
109
+
110
+
111
+
112
+if __name__ == '__main__':
113
+	sqllex = SQLLexer()
114
+	sqllex.build()
115
+	tok_counter = sqllex.getTokensHash()
116
+	while True:
117
+		try: 
118
+			s = raw_input("sql> ")
119
+		except EOFError:
120
+			break
121
+		for tok in sqllex.tokenize(s):
122
+	 		print tok
123
+	 		tok_counter[tok.type] += 1
124
+
125
+	 	print tok_counter

+ 127
- 0
SQLParser.py View File

@@ -0,0 +1,127 @@
1
+from SQLLexer import *
2
+import ply.yacc as yacc
3
+
4
+
5
+class SQLParser(object):
6
+
7
+
8
+	def __init__(self):
9
+
10
+		self.sqllex = SQLLexer()
11
+		self.sqllex.build()
12
+		#lexer = lex.lex(object=sqllex)
13
+
14
+		self.tokens = self.sqllex.tokens
15
+		self.parser = yacc.yacc(module=self)
16
+	
17
+
18
+	def p_select(self, p):
19
+		'select : SELECT columns FROM tables WHERE where_expression SEMI'
20
+		p[0] = ('select-statement', p[2], p[4], p[6])
21
+
22
+	def p_columns_wild(self, p):
23
+		'''columns 	: WILD
24
+					| id_list'''
25
+
26
+		p[0] = ('columns-list', p[1])
27
+
28
+
29
+	def p_id_list(self, p):
30
+		'''id_list 	'''
31
+		# Use the following expressions to construct the AST as guide
32
+		if len(p) > 2:
33
+			p[0] = p[1] + ',' + p[3]
34
+		else:
35
+			p[0] =  p[1]
36
+
37
+
38
+	def p_tables(self, p):
39
+		'tables : id_list'
40
+		p[0] = ('table-list', p[1])
41
+
42
+	def p_where_expression(self, p):
43
+		'''where_expression : expression'''
44
+		p[0] = ('where-expression', p[1])
45
+
46
+	def p_expression(self, p):
47
+		'''expression 	: simple_expression AND expression
48
+						| simple_expression OR expression
49
+						| simple_expression'''
50
+
51
+		if len(p) < 3:
52
+			p[0] = p[1]
53
+		else:
54
+			p[0] = ('logical-expression', p[2], p[1], p[3])
55
+
56
+
57
+	def p_simple_expression(self, p):
58
+		'''simple_expression : factor EQ factor
59
+							| factor LT factor
60
+							| factor'''
61
+
62
+		if len(p) < 3:
63
+			p[0] = p[1]
64
+		else:
65
+			p[0] = ('comparison-expression', p[2], p[1], p[3])
66
+
67
+	def p_factor_id(self, p):
68
+		'''factor : '''
69
+		p[0] = ('ID', p[1])
70
+
71
+	def p_factor_lit(self, p):
72
+		'''factor : '''
73
+		# Note that we do not add the actual literal value because 
74
+		# for the purpose of this module the input literals and numbers
75
+		# can change.
76
+		p[0] = ('LITERAL')
77
+
78
+	def p_factor_num(self, p):
79
+		'''factor : '''
80
+		# Note that we do not add the actual number value because 
81
+		# for the purpose of this module the input literals and numbers
82
+		# can change.
83
+		p[0] = ('NUMBER')
84
+
85
+	def p_factor_expression(self, p):
86
+		'''factor 	: '''
87
+		p[0] = ('group-expression', p[2])
88
+
89
+	def p_error(self, p):
90
+	    print("Syntax error in input!")
91
+	    raise 
92
+
93
+
94
+	def parse(self, text):
95
+		return self.parser.parse(text, lexer=self.sqllex)
96
+
97
+	def print_ast(self, ast):
98
+		Q = ast
99
+		while len(Q) > 0:
100
+			NQ = []
101
+			for node in Q:
102
+				if type(node) == tuple:
103
+					print node[0],  
104
+					for i in range(1, len(node)):
105
+						NQ.append(node[i])
106
+				else:
107
+					print node,
108
+			Q = NQ
109
+			print
110
+
111
+
112
+	
113
+if __name__ == '__main__':	
114
+	parser = SQLParser()
115
+	while True:
116
+		try: 
117
+			s = raw_input("sql> ")
118
+		except EOFError:
119
+			break
120
+
121
+		if not s: continue
122
+		try:
123
+			ast = parser.parse(s)
124
+			parser.print_ast(ast)
125
+		except:
126
+			print "Syntax error"
127
+	   	

BIN
images/tree1.jpg View File


BIN
images/tree2.jpg View File


BIN
images/tree3.jpg View File