%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.torque.engine.database.transform.SQLToAppData |
|
|
1 | package org.apache.torque.engine.database.transform; |
|
2 | ||
3 | /* |
|
4 | * Copyright 2001-2004 The Apache Software Foundation. |
|
5 | * |
|
6 | * Licensed under the Apache License, Version 2.0 (the "License") |
|
7 | * you may not use this file except in compliance with the License. |
|
8 | * You may obtain a copy of the License at |
|
9 | * |
|
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 | * |
|
12 | * Unless required by applicable law or agreed to in writing, software |
|
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 | * See the License for the specific language governing permissions and |
|
16 | * limitations under the License. |
|
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 | 0 | { |
62 | 0 | this.sqlFile = sqlFile; |
63 | 0 | } |
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 | 0 | { |
77 | 0 | this.sqlFile = sqlFile; |
78 | 0 | this.databaseType = databaseType; |
79 | 0 | this.basePropsFilePath = basePropsFilePath; |
80 | 0 | } |
81 | ||
82 | /** |
|
83 | * Get the current input sql file |
|
84 | * |
|
85 | * @return the sql file |
|
86 | */ |
|
87 | public String getSqlFile() |
|
88 | { |
|
89 | 0 | 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 | 0 | this.sqlFile = sqlFile; |
100 | 0 | } |
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 | 0 | if (count < tokens.size()) |
111 | { |
|
112 | 0 | token = (Token) tokens.get(count++); |
113 | } |
|
114 | else |
|
115 | { |
|
116 | 0 | throw new ParseException("No More Tokens"); |
117 | } |
|
118 | 0 | } |
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 | 0 | 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 | 0 | 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 | 0 | next(); |
152 | 0 | if (token.getStr().toUpperCase().equals("TABLE")) |
153 | { |
|
154 | 0 | create_Table(); |
155 | } |
|
156 | 0 | } |
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 | 0 | next(); |
166 | 0 | String tableName = token.getStr(); // name of the table |
167 | 0 | next(); |
168 | 0 | if (!token.getStr().equals("(")) |
169 | { |
|
170 | 0 | err("( expected"); |
171 | } |
|
172 | 0 | next(); |
173 | ||
174 | 0 | Table tbl = new Table (tableName); |
175 | //tbl.setIdMethod("none"); |
|
176 | 0 | while (!token.getStr().equals(";")) |
177 | { |
|
178 | 0 | create_Table_Column(tbl); |
179 | } |
|
180 | ||
181 | 0 | if (tbl.getPrimaryKey().size() == 1) |
182 | { |
|
183 | 0 | tbl.setIdMethod(IDMethod.ID_BROKER); |
184 | } |
|
185 | else |
|
186 | { |
|
187 | 0 | tbl.setIdMethod(IDMethod.NO_ID_METHOD); |
188 | } |
|
189 | 0 | appDataDB.addTable (tbl); |
190 | 0 | } |
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 | // The token should be the first item |
|
201 | // which is the name of the column or |
|
202 | // PRIMARY/FOREIGN/UNIQUE |
|
203 | 0 | if (token.getStr().equals(",")) |
204 | { |
|
205 | 0 | next(); |
206 | } |
|
207 | ||
208 | 0 | if (token.getStr().toUpperCase().equals("PRIMARY")) |
209 | { |
|
210 | 0 | create_Table_Column_Primary(tbl); |
211 | } |
|
212 | 0 | else if (token.getStr().toUpperCase().equals("FOREIGN")) |
213 | { |
|
214 | 0 | create_Table_Column_Foreign(tbl); |
215 | } |
|
216 | 0 | else if (token.getStr().toUpperCase().equals("UNIQUE")) |
217 | { |
|
218 | 0 | create_Table_Column_Unique(tbl); |
219 | } |
|
220 | else |
|
221 | { |
|
222 | 0 | create_Table_Column_Data(tbl); |
223 | } |
|
224 | 0 | } |
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 | 0 | next(); |
234 | 0 | if (!token.getStr().toUpperCase().equals("KEY")) |
235 | { |
|
236 | 0 | err("KEY expected"); |
237 | } |
|
238 | 0 | next(); |
239 | 0 | if (!token.getStr().toUpperCase().equals("(")) |
240 | { |
|
241 | 0 | err("( expected"); |
242 | } |
|
243 | 0 | next(); |
244 | ||
245 | 0 | String colName = token.getStr(); |
246 | 0 | Column c = tbl.getColumn(colName); |
247 | 0 | if (c == null) |
248 | { |
|
249 | 0 | err("Invalid column name: " + colName); |
250 | } |
|
251 | 0 | c.setPrimaryKey(true); |
252 | 0 | next(); |
253 | 0 | while (token.getStr().equals(",")) |
254 | { |
|
255 | 0 | next(); |
256 | 0 | colName = token.getStr(); |
257 | 0 | c = tbl.getColumn(colName); |
258 | 0 | if (c == null) |
259 | { |
|
260 | 0 | err("Invalid column name: " + colName); |
261 | } |
|
262 | 0 | c.setPrimaryKey(true); |
263 | 0 | next(); |
264 | } |
|
265 | ||
266 | 0 | if (!token.getStr().toUpperCase().equals(")")) |
267 | { |
|
268 | 0 | err(") expected"); |
269 | } |
|
270 | 0 | next(); // skip the ) |
271 | 0 | } |
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 | 0 | next(); |
281 | 0 | if (!token.getStr().toUpperCase().equals("(")) |
282 | { |
|
283 | 0 | err("( expected"); |
284 | } |
|
285 | 0 | next(); |
286 | 0 | while (!token.getStr().equals(")")) |
287 | { |
|
288 | 0 | if (!token.getStr().equals(",")) |
289 | { |
|
290 | 0 | String colName = token.getStr(); |
291 | 0 | Column c = tbl.getColumn(colName); |
292 | 0 | if (c == null) |
293 | { |
|
294 | 0 | err("Invalid column name: " + colName); |
295 | } |
|
296 | 0 | c.setUnique(true); |
297 | } |
|
298 | 0 | next(); |
299 | } |
|
300 | 0 | if (!token.getStr().toUpperCase().equals(")")) |
301 | { |
|
302 | 0 | err(") expected got: " + token.getStr()); |
303 | } |
|
304 | ||
305 | 0 | next(); // skip the ) |
306 | 0 | } |
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 | 0 | next(); |
316 | 0 | if (!token.getStr().toUpperCase().equals("KEY")) |
317 | { |
|
318 | 0 | err("KEY expected"); |
319 | } |
|
320 | 0 | next(); |
321 | 0 | if (!token.getStr().toUpperCase().equals("(")) |
322 | { |
|
323 | 0 | err("( expected"); |
324 | } |
|
325 | 0 | next(); |
326 | ||
327 | 0 | ForeignKey fk = new ForeignKey(); |
328 | 0 | List localColumns = new ArrayList(); |
329 | 0 | tbl.addForeignKey(fk); |
330 | ||
331 | 0 | String colName = token.getStr(); |
332 | 0 | localColumns.add(colName); |
333 | 0 | next(); |
334 | 0 | while (token.getStr().equals(",")) |
335 | { |
|
336 | 0 | next(); |
337 | 0 | colName = token.getStr(); |
338 | 0 | localColumns.add(colName); |
339 | 0 | next(); |
340 | } |
|
341 | 0 | if (!token.getStr().toUpperCase().equals(")")) |
342 | { |
|
343 | 0 | err(") expected"); |
344 | } |
|
345 | ||
346 | 0 | next(); |
347 | ||
348 | 0 | if (!token.getStr().toUpperCase().equals("REFERENCES")) |
349 | { |
|
350 | 0 | err("REFERENCES expected"); |
351 | } |
|
352 | ||
353 | 0 | next(); |
354 | ||
355 | 0 | fk.setForeignTableName(token.getStr()); |
356 | ||
357 | 0 | next(); |
358 | ||
359 | 0 | if (token.getStr().toUpperCase().equals("(")) |
360 | { |
|
361 | 0 | next(); |
362 | 0 | int i = 0; |
363 | 0 | fk.addReference((String) localColumns.get(i++), token.getStr()); |
364 | 0 | next(); |
365 | 0 | while (token.getStr().equals(",")) |
366 | { |
|
367 | 0 | next(); |
368 | 0 | fk.addReference((String) localColumns.get(i++), token.getStr()); |
369 | 0 | next(); |
370 | } |
|
371 | 0 | if (!token.getStr().toUpperCase().equals(")")) |
372 | { |
|
373 | 0 | err(") expected"); |
374 | } |
|
375 | 0 | next(); |
376 | } |
|
377 | 0 | } |
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 | 0 | String columnSize = null; |
387 | 0 | String columnPrecision = null; |
388 | 0 | String columnDefault = null; |
389 | 0 | boolean inEnum = false; |
390 | ||
391 | 0 | String columnName = token.getStr(); |
392 | 0 | next(); |
393 | 0 | String columnType = token.getStr(); |
394 | ||
395 | 0 | if (columnName.equals(")") && columnType.equals(";")) |
396 | { |
|
397 | 0 | return; |
398 | } |
|
399 | ||
400 | 0 | next(); |
401 | ||
402 | // special case for MySQL ENUM's which are stupid anyway |
|
403 | // and not properly handled by Torque. |
|
404 | 0 | if (columnType.toUpperCase().equals("ENUM")) |
405 | { |
|
406 | 0 | inEnum = true; |
407 | 0 | next(); // skip ( |
408 | 0 | while (!token.getStr().equals(")")) |
409 | { |
|
410 | // skip until ) |
|
411 | 0 | next(); |
412 | } |
|
413 | 0 | while (!token.getStr().equals(",")) |
414 | { |
|
415 | 0 | if (token.getStr().toUpperCase().equals("DEFAULT")) |
416 | { |
|
417 | 0 | next(); |
418 | 0 | if (token.getStr().equals("'")) |
419 | { |
|
420 | 0 | next(); |
421 | } |
|
422 | 0 | columnDefault = token.getStr(); |
423 | 0 | next(); |
424 | 0 | if (token.getStr().equals("'")) |
425 | { |
|
426 | 0 | next(); |
427 | } |
|
428 | } |
|
429 | // skip until , |
|
430 | 0 | next(); |
431 | } |
|
432 | 0 | next(); // skip , |
433 | 0 | columnType = "VARCHAR"; |
434 | } |
|
435 | 0 | else if (token.getStr().toUpperCase().equals("(")) |
436 | { |
|
437 | 0 | next(); |
438 | 0 | columnSize = token.getStr(); |
439 | 0 | next(); |
440 | 0 | if (token.getStr().equals(",")) |
441 | { |
|
442 | 0 | next(); |
443 | 0 | columnPrecision = token.getStr(); |
444 | 0 | next(); |
445 | } |
|
446 | ||
447 | 0 | if (!token.getStr().equals(")")) |
448 | { |
|
449 | 0 | err(") expected"); |
450 | } |
|
451 | 0 | next(); |
452 | } |
|
453 | ||
454 | 0 | Column col = new Column(columnName); |
455 | 0 | if (columnPrecision != null) |
456 | { |
|
457 | 0 | columnSize = columnSize + columnPrecision; |
458 | } |
|
459 | 0 | col.setTypeFromString(columnType, columnSize); |
460 | 0 | tbl.addColumn(col); |
461 | ||
462 | 0 | if (inEnum) |
463 | { |
|
464 | 0 | col.setNotNull(true); |
465 | 0 | if (columnDefault != null) |
466 | { |
|
467 | 0 | col.setDefaultValue(columnDefault); |
468 | } |
|
469 | } |
|
470 | else |
|
471 | { |
|
472 | 0 | while (!token.getStr().equals(",") && !token.getStr().equals(")")) |
473 | { |
|
474 | 0 | if (token.getStr().toUpperCase().equals("NOT")) |
475 | { |
|
476 | 0 | next(); |
477 | 0 | if (!token.getStr().toUpperCase().equals("NULL")) |
478 | { |
|
479 | 0 | err("NULL expected after NOT"); |
480 | } |
|
481 | 0 | col.setNotNull(true); |
482 | 0 | next(); |
483 | } |
|
484 | 0 | else if (token.getStr().toUpperCase().equals("PRIMARY")) |
485 | { |
|
486 | 0 | next(); |
487 | 0 | if (!token.getStr().toUpperCase().equals("KEY")) |
488 | { |
|
489 | 0 | err("KEY expected after PRIMARY"); |
490 | } |
|
491 | 0 | col.setPrimaryKey(true); |
492 | 0 | next(); |
493 | } |
|
494 | 0 | else if (token.getStr().toUpperCase().equals("UNIQUE")) |
495 | { |
|
496 | 0 | col.setUnique(true); |
497 | 0 | next(); |
498 | } |
|
499 | 0 | else if (token.getStr().toUpperCase().equals("NULL")) |
500 | { |
|
501 | 0 | col.setNotNull(false); |
502 | 0 | next(); |
503 | } |
|
504 | 0 | else if (token.getStr().toUpperCase().equals("AUTO_INCREMENT")) |
505 | { |
|
506 | 0 | col.setAutoIncrement(true); |
507 | 0 | next(); |
508 | } |
|
509 | 0 | else if (token.getStr().toUpperCase().equals("DEFAULT")) |
510 | { |
|
511 | 0 | next(); |
512 | 0 | if (token.getStr().equals("'")) |
513 | { |
|
514 | 0 | next(); |
515 | } |
|
516 | 0 | col.setDefaultValue(token.getStr()); |
517 | 0 | next(); |
518 | 0 | if (token.getStr().equals("'")) |
519 | { |
|
520 | 0 | next(); |
521 | } |
|
522 | } |
|
523 | } |
|
524 | 0 | next(); // eat the , |
525 | } |
|
526 | 0 | } |
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 | 0 | count = 0; |
537 | 0 | appData = new AppData(databaseType, basePropsFilePath); |
538 | 0 | appDataDB = new Database(); |
539 | 0 | appData.addDatabase(appDataDB); |
540 | ||
541 | 0 | FileReader fr = new FileReader(sqlFile); |
542 | 0 | BufferedReader br = new BufferedReader(fr); |
543 | 0 | SQLScanner scanner = new SQLScanner(br); |
544 | ||
545 | 0 | tokens = scanner.scan(); |
546 | ||
547 | 0 | br.close(); |
548 | ||
549 | 0 | while (hasTokens()) |
550 | { |
|
551 | 0 | if (token == null) |
552 | { |
|
553 | 0 | next(); |
554 | } |
|
555 | ||
556 | 0 | if (token.getStr().toUpperCase().equals("CREATE")) |
557 | { |
|
558 | 0 | create(); |
559 | } |
|
560 | 0 | if (hasTokens()) |
561 | { |
|
562 | 0 | next(); |
563 | } |
|
564 | } |
|
565 | 0 | 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 | 0 | SQLToAppData s2a = new SQLToAppData(args[0]); |
577 | 0 | AppData ad = s2a.execute(); |
578 | 0 | System.out.println(ad); |
579 | 0 | } |
580 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |