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