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