Coverage Report - org.apache.torque.engine.database.transform.SQLToAppData
 
Classes in this File Line Coverage Branch Coverage Complexity
SQLToAppData
0%
0/225
0%
0/122
5.267
 
 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,v 1.1 2007-10-21 07:57:26 abyrne Exp $
 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  0
     {
 77  0
         this.sqlFile = sqlFile;
 78  0
         this.databaseType = databaseType;
 79  0
     }
 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  0
         if (count < tokens.size())
 109  
         {
 110  0
             token = (Token) tokens.get(count++);
 111  
         }
 112  
         else
 113  
         {
 114  0
             throw new ParseException("No More Tokens");
 115  
         }
 116  0
     }
 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  0
         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  0
         next();
 150  0
         if (token.getStr().toUpperCase().equals("TABLE"))
 151  
         {
 152  0
             create_Table();
 153  
         }
 154  0
     }
 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  0
         next();
 164  0
         String tableName = token.getStr(); // name of the table
 165  0
         next();
 166  0
         if (!token.getStr().equals("("))
 167  
         {
 168  0
             err("( expected");
 169  
         }
 170  0
         next();
 171  
 
 172  0
         Table tbl = new Table (tableName);
 173  
         //tbl.setIdMethod("none");
 174  0
         while (!token.getStr().equals(";"))
 175  
         {
 176  0
             create_Table_Column(tbl);
 177  
         }
 178  
 
 179  0
         if (tbl.getPrimaryKey().size() == 1)
 180  
         {
 181  0
             tbl.setIdMethod(IDMethod.ID_BROKER);
 182  
         }
 183  
         else
 184  
         {
 185  0
             tbl.setIdMethod(IDMethod.NO_ID_METHOD);
 186  
         }
 187  0
         appDataDB.addTable (tbl);
 188  0
     }
 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  0
         if (token.getStr().equals(","))
 202  
         {
 203  0
             next();
 204  
         }
 205  
 
 206  0
         if (token.getStr().toUpperCase().equals("PRIMARY"))
 207  
         {
 208  0
             create_Table_Column_Primary(tbl);
 209  
         }
 210  0
         else if (token.getStr().toUpperCase().equals("FOREIGN"))
 211  
         {
 212  0
             create_Table_Column_Foreign(tbl);
 213  
         }
 214  0
         else if (token.getStr().toUpperCase().equals("UNIQUE"))
 215  
         {
 216  0
             create_Table_Column_Unique(tbl);
 217  
         }
 218  
         else
 219  
         {
 220  0
             create_Table_Column_Data(tbl);
 221  
         }
 222  0
     }
 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  0
         next();
 232  0
         if (!token.getStr().toUpperCase().equals("KEY"))
 233  
         {
 234  0
             err("KEY expected");
 235  
         }
 236  0
         next();
 237  0
         if (!token.getStr().toUpperCase().equals("("))
 238  
         {
 239  0
             err("( expected");
 240  
         }
 241  0
         next();
 242  
 
 243  0
         String colName = token.getStr();
 244  0
         Column c = tbl.getColumn(colName);
 245  0
         if (c == null)
 246  
         {
 247  0
             err("Invalid column name: " + colName);
 248  
         }
 249  0
         c.setPrimaryKey(true);
 250  0
         next();
 251  0
         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  
         }
 263  
 
 264  0
         if (!token.getStr().toUpperCase().equals(")"))
 265  
         {
 266  0
             err(") expected");
 267  
         }
 268  0
         next(); // skip the )
 269  0
     }
 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  
         }
 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  
         }
 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  
             }
 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  0
         String columnSize = null;
 385  0
         String columnPrecision = null;
 386  0
         String columnDefault = null;
 387  0
         boolean inEnum = false;
 388  
 
 389  0
         String columnName = token.getStr();
 390  0
         next();
 391  0
         String columnType = token.getStr();
 392  
 
 393  0
         if (columnName.equals(")") && columnType.equals(";"))
 394  
         {
 395  0
             return;
 396  
         }
 397  
 
 398  0
         next();
 399  
 
 400  
         // special case for MySQL ENUM's which are stupid anyway
 401  
         // and not properly handled by Torque.
 402  0
         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  
             }
 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  
             }
 430  0
             next(); // skip ,
 431  0
             columnType = "VARCHAR";
 432  
         }
 433  0
         else if (token.getStr().toUpperCase().equals("("))
 434  
         {
 435  0
             next();
 436  0
             columnSize = token.getStr();
 437  0
             next();
 438  0
             if (token.getStr().equals(","))
 439  
             {
 440  0
                 next();
 441  0
                 columnPrecision = token.getStr();
 442  0
                 next();
 443  
             }
 444  
 
 445  0
             if (!token.getStr().equals(")"))
 446  
             {
 447  0
                 err(") expected");
 448  
             }
 449  0
             next();
 450  
         }
 451  
 
 452  0
         Column col = new Column(columnName);
 453  0
         if (columnPrecision != null)
 454  
         {
 455  0
             columnSize = columnSize + columnPrecision;
 456  
         }
 457  0
         col.setTypeFromString(columnType, columnSize);
 458  0
         tbl.addColumn(col);
 459  
 
 460  0
         if (inEnum)
 461  
         {
 462  0
             col.setNotNull(true);
 463  0
             if (columnDefault != null)
 464  
             {
 465  0
                 col.setDefaultValue(columnDefault);
 466  
             }
 467  
         }
 468  
         else
 469  
         {
 470  0
             while (!token.getStr().equals(",") && !token.getStr().equals(")"))
 471  
             {
 472  0
                 if (token.getStr().toUpperCase().equals("NOT"))
 473  
                 {
 474  0
                     next();
 475  0
                     if (!token.getStr().toUpperCase().equals("NULL"))
 476  
                     {
 477  0
                         err("NULL expected after NOT");
 478  
                     }
 479  0
                     col.setNotNull(true);
 480  0
                     next();
 481  
                 }
 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  
                 }
 492  0
                 else if (token.getStr().toUpperCase().equals("UNIQUE"))
 493  
                 {
 494  0
                     col.setUnique(true);
 495  0
                     next();
 496  
                 }
 497  0
                 else if (token.getStr().toUpperCase().equals("NULL"))
 498  
                 {
 499  0
                     col.setNotNull(false);
 500  0
                     next();
 501  
                 }
 502  0
                 else if (token.getStr().toUpperCase().equals("AUTO_INCREMENT"))
 503  
                 {
 504  0
                     col.setAutoIncrement(true);
 505  0
                     next();
 506  
                 }
 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  
                     }
 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  
                         }
 532  
                     }
 533  0
                     throw new ParseException("Error parsing line "
 534  
                             + line + " : Unknown token Nr. "
 535  
                             + count
 536  
                             + " : "
 537  
                             + token);
 538  
                 }
 539  
             }
 540  0
             next(); // eat the ,
 541  
         }
 542  0
     }
 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  0
         count = 0;
 553  0
         appDataDB = new Database(databaseType);
 554  
 
 555  0
         FileReader fr = new FileReader(sqlFile);
 556  0
         BufferedReader br = new BufferedReader(fr);
 557  0
         SQLScanner scanner = new SQLScanner(br);
 558  
 
 559  0
         tokens = scanner.scan();
 560  
 
 561  0
         br.close();
 562  
 
 563  0
         while (hasTokens())
 564  
         {
 565  0
             if (token == null)
 566  
             {
 567  0
                 next();
 568  
             }
 569  
 
 570  0
             if (token.getStr().toUpperCase().equals("CREATE"))
 571  
             {
 572  0
                 create();
 573  
             }
 574  0
             if (hasTokens())
 575  
             {
 576  0
                 next();
 577  
             }
 578  
         }
 579  0
         return appDataDB;
 580  
     }
 581  
 }