1 package org.apache.torque.engine.database.transform;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.io.BufferedReader;
20 import java.io.FileReader;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.List;
24 import org.apache.torque.engine.database.model.AppData;
25 import org.apache.torque.engine.database.model.Column;
26 import org.apache.torque.engine.database.model.Database;
27 import org.apache.torque.engine.database.model.ForeignKey;
28 import org.apache.torque.engine.database.model.IDMethod;
29 import org.apache.torque.engine.database.model.Table;
30 import org.apache.torque.engine.sql.ParseException;
31 import org.apache.torque.engine.sql.SQLScanner;
32 import org.apache.torque.engine.sql.Token;
33
34 /***
35 * A Class that converts an sql input file to an AppData
36 * structure. The class makes use of SQL Scanner to get
37 * sql tokens and the parses these to create the AppData
38 * class. SQLToAppData is in effect a simplified sql parser.
39 *
40 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
41 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
42 * @version $Id: SQLToAppData.java,v 1.2.2.2 2004/05/20 04:34:17 seade Exp $
43 */
44 public class SQLToAppData
45 {
46 private String sqlFile;
47 private List tokens;
48 private Token token;
49 private AppData appData;
50 private Database appDataDB;
51 private int count;
52 private String databaseType;
53 private String basePropsFilePath;
54
55 /***
56 * Create a new class with an input Reader
57 *
58 * @param sqlFile the sql file
59 */
60 public SQLToAppData(String sqlFile)
61 {
62 this.sqlFile = sqlFile;
63 }
64
65 /***
66 * Create a new class with an input Reader. This ctor is not used
67 * but putting here in the event db.props properties are found to
68 * be useful converting sql to xml, the infrastructure will exist
69 *
70 * @param sqlFile the sql file
71 * @param databaseType
72 * @param basePropsFilePath
73 */
74 public SQLToAppData(String sqlFile, String databaseType,
75 String basePropsFilePath)
76 {
77 this.sqlFile = sqlFile;
78 this.databaseType = databaseType;
79 this.basePropsFilePath = basePropsFilePath;
80 }
81
82 /***
83 * Get the current input sql file
84 *
85 * @return the sql file
86 */
87 public String getSqlFile()
88 {
89 return sqlFile;
90 }
91
92 /***
93 * Set the current input sql file
94 *
95 * @param sqlFile the sql file
96 */
97 public void setSqlFile(String sqlFile)
98 {
99 this.sqlFile = sqlFile;
100 }
101
102 /***
103 * Move to the next token. Throws an exception
104 * if there is no more tokens available.
105 *
106 * @throws ParseException
107 */
108 private void next() throws ParseException
109 {
110 if (count < tokens.size())
111 {
112 token = (Token) tokens.get(count++);
113 }
114 else
115 {
116 throw new ParseException("No More Tokens");
117 }
118 }
119
120 /***
121 * Creates an error condition and adds the line and
122 * column number of the current token to the error
123 * message.
124 *
125 * @param name name of the error
126 * @throws ParseException
127 */
128 private void err(String name) throws ParseException
129 {
130 throw new ParseException (name + " at [ line: " + token.getLine()
131 + " col: " + token.getCol() + " ]");
132 }
133
134 /***
135 * Check if there is more tokens available for parsing.
136 *
137 * @return true if there are more tokens available
138 */
139 private boolean hasTokens()
140 {
141 return count < tokens.size();
142 }
143
144 /***
145 * Parses a CREATE TABLE FOO command.
146 *
147 * @throws ParseException
148 */
149 private void create() throws ParseException
150 {
151 next();
152 if (token.getStr().toUpperCase().equals("TABLE"))
153 {
154 create_Table();
155 }
156 }
157
158 /***
159 * Parses a CREATE TABLE sql command
160 *
161 * @throws ParseException error parsing the input file
162 */
163 private void create_Table() throws ParseException
164 {
165 next();
166 String tableName = token.getStr();
167 next();
168 if (!token.getStr().equals("("))
169 {
170 err("( expected");
171 }
172 next();
173
174 Table tbl = new Table (tableName);
175
176 while (!token.getStr().equals(";"))
177 {
178 create_Table_Column(tbl);
179 }
180
181 if (tbl.getPrimaryKey().size() == 1)
182 {
183 tbl.setIdMethod(IDMethod.ID_BROKER);
184 }
185 else
186 {
187 tbl.setIdMethod(IDMethod.NO_ID_METHOD);
188 }
189 appDataDB.addTable (tbl);
190 }
191
192 /***
193 * Parses column information between the braces of a CREATE
194 * TABLE () sql statement.
195 *
196 * @throws ParseException error parsing the input file
197 */
198 private void create_Table_Column(Table tbl) throws ParseException
199 {
200
201
202
203 if (token.getStr().equals(","))
204 {
205 next();
206 }
207
208 if (token.getStr().toUpperCase().equals("PRIMARY"))
209 {
210 create_Table_Column_Primary(tbl);
211 }
212 else if (token.getStr().toUpperCase().equals("FOREIGN"))
213 {
214 create_Table_Column_Foreign(tbl);
215 }
216 else if (token.getStr().toUpperCase().equals("UNIQUE"))
217 {
218 create_Table_Column_Unique(tbl);
219 }
220 else
221 {
222 create_Table_Column_Data(tbl);
223 }
224 }
225
226 /***
227 * Parses PRIMARY KEY (FOO,BAR) statement
228 *
229 * @throws ParseException error parsing the input file
230 */
231 private void create_Table_Column_Primary (Table tbl) throws ParseException
232 {
233 next();
234 if (!token.getStr().toUpperCase().equals("KEY"))
235 {
236 err("KEY expected");
237 }
238 next();
239 if (!token.getStr().toUpperCase().equals("("))
240 {
241 err("( expected");
242 }
243 next();
244
245 String colName = token.getStr();
246 Column c = tbl.getColumn(colName);
247 if (c == null)
248 {
249 err("Invalid column name: " + colName);
250 }
251 c.setPrimaryKey(true);
252 next();
253 while (token.getStr().equals(","))
254 {
255 next();
256 colName = token.getStr();
257 c = tbl.getColumn(colName);
258 if (c == null)
259 {
260 err("Invalid column name: " + colName);
261 }
262 c.setPrimaryKey(true);
263 next();
264 }
265
266 if (!token.getStr().toUpperCase().equals(")"))
267 {
268 err(") expected");
269 }
270 next();
271 }
272
273 /***
274 * Parses UNIQUE (NAME,FOO,BAR) statement
275 *
276 * @throws ParseException error parsing the input file
277 */
278 private void create_Table_Column_Unique(Table tbl) throws ParseException
279 {
280 next();
281 if (!token.getStr().toUpperCase().equals("("))
282 {
283 err("( expected");
284 }
285 next();
286 while (!token.getStr().equals(")"))
287 {
288 if (!token.getStr().equals(","))
289 {
290 String colName = token.getStr();
291 Column c = tbl.getColumn(colName);
292 if (c == null)
293 {
294 err("Invalid column name: " + colName);
295 }
296 c.setUnique(true);
297 }
298 next();
299 }
300 if (!token.getStr().toUpperCase().equals(")"))
301 {
302 err(") expected got: " + token.getStr());
303 }
304
305 next();
306 }
307
308 /***
309 * Parses FOREIGN KEY (BAR) REFERENCES TABLE (BAR) statement
310 *
311 * @throws ParseException error parsing the input file
312 */
313 private void create_Table_Column_Foreign(Table tbl) throws ParseException
314 {
315 next();
316 if (!token.getStr().toUpperCase().equals("KEY"))
317 {
318 err("KEY expected");
319 }
320 next();
321 if (!token.getStr().toUpperCase().equals("("))
322 {
323 err("( expected");
324 }
325 next();
326
327 ForeignKey fk = new ForeignKey();
328 List localColumns = new ArrayList();
329 tbl.addForeignKey(fk);
330
331 String colName = token.getStr();
332 localColumns.add(colName);
333 next();
334 while (token.getStr().equals(","))
335 {
336 next();
337 colName = token.getStr();
338 localColumns.add(colName);
339 next();
340 }
341 if (!token.getStr().toUpperCase().equals(")"))
342 {
343 err(") expected");
344 }
345
346 next();
347
348 if (!token.getStr().toUpperCase().equals("REFERENCES"))
349 {
350 err("REFERENCES expected");
351 }
352
353 next();
354
355 fk.setForeignTableName(token.getStr());
356
357 next();
358
359 if (token.getStr().toUpperCase().equals("("))
360 {
361 next();
362 int i = 0;
363 fk.addReference((String) localColumns.get(i++), token.getStr());
364 next();
365 while (token.getStr().equals(","))
366 {
367 next();
368 fk.addReference((String) localColumns.get(i++), token.getStr());
369 next();
370 }
371 if (!token.getStr().toUpperCase().equals(")"))
372 {
373 err(") expected");
374 }
375 next();
376 }
377 }
378
379 /***
380 * Parse the data definition of the column statement.
381 *
382 * @throws ParseException error parsing the input file
383 */
384 private void create_Table_Column_Data(Table tbl) throws ParseException
385 {
386 String columnSize = null;
387 String columnPrecision = null;
388 String columnDefault = null;
389 boolean inEnum = false;
390
391 String columnName = token.getStr();
392 next();
393 String columnType = token.getStr();
394
395 if (columnName.equals(")") && columnType.equals(";"))
396 {
397 return;
398 }
399
400 next();
401
402
403
404 if (columnType.toUpperCase().equals("ENUM"))
405 {
406 inEnum = true;
407 next();
408 while (!token.getStr().equals(")"))
409 {
410
411 next();
412 }
413 while (!token.getStr().equals(","))
414 {
415 if (token.getStr().toUpperCase().equals("DEFAULT"))
416 {
417 next();
418 if (token.getStr().equals("'"))
419 {
420 next();
421 }
422 columnDefault = token.getStr();
423 next();
424 if (token.getStr().equals("'"))
425 {
426 next();
427 }
428 }
429
430 next();
431 }
432 next();
433 columnType = "VARCHAR";
434 }
435 else if (token.getStr().toUpperCase().equals("("))
436 {
437 next();
438 columnSize = token.getStr();
439 next();
440 if (token.getStr().equals(","))
441 {
442 next();
443 columnPrecision = token.getStr();
444 next();
445 }
446
447 if (!token.getStr().equals(")"))
448 {
449 err(") expected");
450 }
451 next();
452 }
453
454 Column col = new Column(columnName);
455 if (columnPrecision != null)
456 {
457 columnSize = columnSize + columnPrecision;
458 }
459 col.setTypeFromString(columnType, columnSize);
460 tbl.addColumn(col);
461
462 if (inEnum)
463 {
464 col.setNotNull(true);
465 if (columnDefault != null)
466 {
467 col.setDefaultValue(columnDefault);
468 }
469 }
470 else
471 {
472 while (!token.getStr().equals(",") && !token.getStr().equals(")"))
473 {
474 if (token.getStr().toUpperCase().equals("NOT"))
475 {
476 next();
477 if (!token.getStr().toUpperCase().equals("NULL"))
478 {
479 err("NULL expected after NOT");
480 }
481 col.setNotNull(true);
482 next();
483 }
484 else if (token.getStr().toUpperCase().equals("PRIMARY"))
485 {
486 next();
487 if (!token.getStr().toUpperCase().equals("KEY"))
488 {
489 err("KEY expected after PRIMARY");
490 }
491 col.setPrimaryKey(true);
492 next();
493 }
494 else if (token.getStr().toUpperCase().equals("UNIQUE"))
495 {
496 col.setUnique(true);
497 next();
498 }
499 else if (token.getStr().toUpperCase().equals("NULL"))
500 {
501 col.setNotNull(false);
502 next();
503 }
504 else if (token.getStr().toUpperCase().equals("AUTO_INCREMENT"))
505 {
506 col.setAutoIncrement(true);
507 next();
508 }
509 else if (token.getStr().toUpperCase().equals("DEFAULT"))
510 {
511 next();
512 if (token.getStr().equals("'"))
513 {
514 next();
515 }
516 col.setDefaultValue(token.getStr());
517 next();
518 if (token.getStr().equals("'"))
519 {
520 next();
521 }
522 }
523 }
524 next();
525 }
526 }
527
528 /***
529 * Execute the parser.
530 *
531 * @throws IOException If an I/O error occurs
532 * @throws ParseException error parsing the input file
533 */
534 public AppData execute() throws IOException, ParseException
535 {
536 count = 0;
537 appData = new AppData(databaseType, basePropsFilePath);
538 appDataDB = new Database();
539 appData.addDatabase(appDataDB);
540
541 FileReader fr = new FileReader(sqlFile);
542 BufferedReader br = new BufferedReader(fr);
543 SQLScanner scanner = new SQLScanner(br);
544
545 tokens = scanner.scan();
546
547 br.close();
548
549 while (hasTokens())
550 {
551 if (token == null)
552 {
553 next();
554 }
555
556 if (token.getStr().toUpperCase().equals("CREATE"))
557 {
558 create();
559 }
560 if (hasTokens())
561 {
562 next();
563 }
564 }
565 return appData;
566 }
567
568 /***
569 * Just 4 testing.
570 *
571 * @param args commandline args
572 * @throws Exception an exception
573 */
574 public static void main(String args[]) throws Exception
575 {
576 SQLToAppData s2a = new SQLToAppData(args[0]);
577 AppData ad = s2a.execute();
578 System.out.println(ad);
579 }
580 }