001: /*
002: * Copyright 2002 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: SchemaTable.java,v 1.5 2003/10/18 22:05:16 jackknifebarber Exp $
009: */
010:
011: package com.triactive.jdo.store;
012:
013: import com.triactive.jdo.model.FieldMetaData;
014: import com.triactive.jdo.model.MetaData;
015: import java.util.ArrayList;
016: import java.util.Iterator;
017: import java.util.List;
018: import java.util.ListIterator;
019: import java.util.StringTokenizer;
020: import java.sql.Connection;
021: import java.sql.PreparedStatement;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024: import java.sql.Statement;
025: import javax.jdo.JDODataStoreException;
026: import org.apache.log4j.Category;
027:
028: class SchemaTable extends BaseTable {
029: private static final Category LOG = Category
030: .getInstance(SchemaTable.class);
031:
032: private ColumnMapping tableIDMapping;
033: private ColumnMapping nextIDMapping;
034: private ColumnMapping javaIDMapping;
035: private ColumnMapping tableNameMapping;
036: private String fetchByJavaIDStmt;
037: private String getMaxTableIDStmt;
038: private String insertStmt;
039: private String fetchByTableIDStmt;
040: private String fetchByTableNameStmt;
041: private String getNextIDStmt;
042: private String incrNextIDStmt;
043: private String fetchAllStmt;
044: private int tableValidationFlags;
045:
046: public SchemaTable(StoreManager storeMgr) {
047: super (storeMgr);
048:
049: name = new TableIdentifier(dba, "jdoTable");
050: }
051:
052: public void initialize() {
053: assertIsUninitialized();
054:
055: Column tableIDColumn = newColumn(int.class, "tableID")
056: .setPrimaryKeyPart();
057: Column nextIDColumn = newColumn(int.class, "nextID");
058: Column javaIDColumn = newColumn(String.class, "javaName")
059: .setMaximumLength(128).setUnique();
060: Column tableNameColumn = newColumn(String.class, "tableName")
061: .setMaximumLength(64).setUnique();
062:
063: tableIDMapping = dba.getMapping(tableIDColumn);
064: nextIDMapping = dba.getMapping(nextIDColumn);
065: javaIDMapping = dba.getMapping(javaIDColumn);
066: tableNameMapping = dba.getMapping(tableNameColumn);
067:
068: fetchByJavaIDStmt = "SELECT " + tableIDColumn.getName() + ","
069: + tableNameColumn.getName() + " FROM " + name
070: + " WHERE " + javaIDColumn.getName() + " = ?";
071:
072: getMaxTableIDStmt = "SELECT MAX(" + tableIDColumn.getName()
073: + ")" + " FROM " + name;
074:
075: insertStmt = "INSERT INTO " + name + " ("
076: + tableIDColumn.getName() + ","
077: + nextIDColumn.getName() + "," + javaIDColumn.getName()
078: + "," + tableNameColumn.getName() + ")"
079: + " VALUES (?,?,?,?)";
080:
081: fetchByTableIDStmt = "SELECT " + javaIDColumn.getName()
082: + " FROM " + name + " WHERE " + tableIDColumn.getName()
083: + " = ?";
084:
085: fetchByTableNameStmt = "SELECT " + tableIDColumn.getName()
086: + " FROM " + name + " WHERE "
087: + tableNameColumn.getName() + " = ?";
088:
089: getNextIDStmt = "SELECT " + nextIDColumn.getName() + " FROM "
090: + name + " WHERE " + tableIDColumn.getName() + " = ?";
091:
092: incrNextIDStmt = "UPDATE " + name + " SET "
093: + nextIDColumn.getName() + " = ?" + " WHERE "
094: + tableIDColumn.getName() + " = ?";
095:
096: fetchAllStmt = "SELECT " + tableIDColumn.getName() + ","
097: + javaIDColumn.getName() + ","
098: + tableNameColumn.getName() + " FROM " + name
099: + " ORDER BY " + tableIDColumn.getName();
100:
101: state = TABLE_STATE_INITIALIZED;
102: }
103:
104: public boolean validate(int flags, Connection conn)
105: throws SQLException {
106: this .tableValidationFlags = flags;
107:
108: return super .validate(flags, conn);
109: }
110:
111: public boolean exists(Connection conn) throws SQLException {
112: assertIsInitialized();
113:
114: try {
115: getNextTableID(conn);
116: return true;
117: } catch (SQLException e) {
118: return false;
119: }
120: }
121:
122: private SQLIdentifier generateTableName(MetaData md, Connection conn)
123: throws SQLException {
124: ArrayList nameParts = new ArrayList();
125:
126: StringTokenizer tokens = new StringTokenizer(md.getJavaName(),
127: ".");
128:
129: while (tokens.hasMoreTokens())
130: nameParts.add(tokens.nextToken());
131:
132: ListIterator li = nameParts.listIterator(nameParts.size());
133:
134: String uniqueName = (String) li.previous();
135:
136: if (md instanceof FieldMetaData)
137: uniqueName = (String) li.previous() + '_' + uniqueName;
138:
139: TableIdentifier tableName = new TableIdentifier(dba, uniqueName);
140: int n = 1;
141:
142: while (tableNameInUse(tableName, conn)) {
143: if (li.hasPrevious())
144: uniqueName = (String) li.previous() + '_' + uniqueName;
145: else
146: uniqueName = uniqueName + n++;
147:
148: tableName = new TableIdentifier(dba, uniqueName);
149: }
150:
151: return tableName;
152: }
153:
154: private boolean tableNameInUse(TableIdentifier tableName,
155: Connection conn) throws SQLException {
156: if (storeMgr.tableExists(tableName, conn))
157: return true;
158:
159: PreparedStatement ps = conn
160: .prepareStatement(fetchByTableNameStmt);
161:
162: try {
163: ps.setString(1, tableName.getSQLIdentifier());
164: ResultSet rs = ps.executeQuery();
165:
166: try {
167: return rs.next();
168: } finally {
169: rs.close();
170: }
171: } finally {
172: ps.close();
173: }
174: }
175:
176: private TableMetadata fetchByJavaID(String javaName, Connection conn)
177: throws SQLException {
178: TableMetadata tmd = null;
179:
180: PreparedStatement ps = conn.prepareStatement(fetchByJavaIDStmt);
181:
182: try {
183: javaIDMapping.setString(null, ps, 1, javaName);
184:
185: long startTime = System.currentTimeMillis();
186:
187: ResultSet rs = ps.executeQuery();
188:
189: try {
190: if (LOG.isDebugEnabled())
191: LOG.debug("Time = "
192: + (System.currentTimeMillis() - startTime)
193: + " ms: " + fetchByJavaIDStmt);
194:
195: storeMgr.logSQLWarnings(ps);
196:
197: if (rs.next()) {
198: int tableID = tableIDMapping.getInt(null, rs, 1);
199: SQLIdentifier tableName = new SQLIdentifier(dba,
200: tableNameMapping.getString(null, rs, 2));
201:
202: storeMgr.logSQLWarnings(rs);
203:
204: tmd = new TableMetadata(tableID, javaName,
205: tableName);
206: }
207: } finally {
208: rs.close();
209: }
210: } finally {
211: ps.close();
212: }
213:
214: return tmd;
215: }
216:
217: private int getNextTableID(Connection conn) throws SQLException {
218: int nextTableID;
219: Statement stmt = conn.createStatement();
220:
221: try {
222: long startTime = System.currentTimeMillis();
223:
224: ResultSet rs = stmt.executeQuery(getMaxTableIDStmt);
225:
226: try {
227: if (LOG.isDebugEnabled())
228: LOG.debug("Time = "
229: + (System.currentTimeMillis() - startTime)
230: + " ms: " + getMaxTableIDStmt);
231:
232: storeMgr.logSQLWarnings(stmt);
233:
234: if (!rs.next())
235: throw new JDODataStoreException(
236: "Can't find max table ID in " + name
237: + "???");
238:
239: nextTableID = rs.getInt(1) + 1;
240: storeMgr.logSQLWarnings(rs);
241: } finally {
242: rs.close();
243: }
244: } finally {
245: stmt.close();
246: }
247:
248: return nextTableID;
249: }
250:
251: private void insert(TableMetadata tmd, Connection conn)
252: throws SQLException {
253: PreparedStatement ps = conn.prepareStatement(insertStmt);
254:
255: try {
256: tableIDMapping.setInt(null, ps, 1, tmd.tableID);
257: nextIDMapping.setInt(null, ps, 2, 0);
258: javaIDMapping.setString(null, ps, 3, tmd.javaName);
259: tableNameMapping.setString(null, ps, 4, tmd.tableName
260: .getSQLIdentifier());
261:
262: long startTime = System.currentTimeMillis();
263:
264: ps.executeUpdate();
265:
266: if (LOG.isDebugEnabled())
267: LOG.debug("Time = "
268: + (System.currentTimeMillis() - startTime)
269: + " ms: " + insertStmt);
270:
271: storeMgr.logSQLWarnings(ps);
272: } finally {
273: ps.close();
274: }
275: }
276:
277: /**
278: * Retrieve the TableMetadata for the given MetaData. If the TableMetaData is
279: * not found in the SchemaTable and <code>tableValidationFlags</code> do not
280: * specify AUTO_CREATE, a <code>MissingSchemaTableEntryException</code> is thrown.
281: *
282: * @param md The MetaData to retrieve the TableMetaData for.
283: * @param conn The Connection to use.
284: *
285: * @return The TableMetadata for the given MetaData.
286: *
287: * @exception SQLException
288: * If inserting the TableMetadata into the SchemaTable failed.
289: * @exception MissingSchemaTableEntryException
290: * The the SchemaTable does not contain a row for the requested MetaData, and
291: * <code>tableValidationFlags</code> does not specifiy AUTO_CREATE.
292: */
293: public TableMetadata getTableMetadata(MetaData md, Connection conn)
294: throws SQLException, MissingSchemaTableEntryException {
295: assertIsValidated();
296:
297: String javaName = md.getJavaName();
298: TableMetadata tmd = fetchByJavaID(javaName, conn);
299:
300: if (tmd == null) {
301: if ((tableValidationFlags & AUTO_CREATE) == 0)
302: throw new MissingSchemaTableEntryException(this ,
303: javaName);
304:
305: int tableID = getNextTableID(conn);
306: SQLIdentifier tableName = generateTableName(md, conn);
307:
308: tmd = new TableMetadata(tableID, javaName, tableName);
309:
310: insert(tmd, conn);
311: }
312:
313: return tmd;
314: }
315:
316: public String getJavaName(int tableID, Connection conn)
317: throws SQLException {
318: assertIsValidated();
319:
320: String javaName = null;
321:
322: PreparedStatement ps = conn
323: .prepareStatement(fetchByTableIDStmt);
324:
325: try {
326: tableIDMapping.setInt(null, ps, 1, tableID);
327:
328: long startTime = System.currentTimeMillis();
329:
330: ResultSet rs = ps.executeQuery();
331:
332: try {
333: if (LOG.isDebugEnabled())
334: LOG.debug("Time = "
335: + (System.currentTimeMillis() - startTime)
336: + " ms: " + fetchByTableIDStmt);
337:
338: storeMgr.logSQLWarnings(ps);
339:
340: if (rs.next()) {
341: javaName = javaIDMapping.getString(null, rs, 1);
342:
343: storeMgr.logSQLWarnings(rs);
344: }
345: } finally {
346: rs.close();
347: }
348: } finally {
349: ps.close();
350: }
351:
352: return javaName;
353: }
354:
355: public int getNextOIDHiValue(int tableID, Connection conn)
356: throws SQLException {
357: assertIsValidated();
358:
359: int nextHiValue;
360: PreparedStatement ps = conn.prepareStatement(getNextIDStmt);
361:
362: try {
363: tableIDMapping.setInt(null, ps, 1, tableID);
364:
365: long startTime = System.currentTimeMillis();
366:
367: ResultSet rs = ps.executeQuery();
368:
369: try {
370: if (LOG.isDebugEnabled())
371: LOG.debug("Time = "
372: + (System.currentTimeMillis() - startTime)
373: + " ms: " + getNextIDStmt);
374:
375: storeMgr.logSQLWarnings(ps);
376:
377: if (!rs.next())
378: throw new JDODataStoreException(
379: "Can't find table ID " + tableID + " in "
380: + getName() + "???");
381:
382: nextHiValue = nextIDMapping.getInt(null, rs, 1);
383: storeMgr.logSQLWarnings(rs);
384: } finally {
385: rs.close();
386: }
387: } finally {
388: ps.close();
389: }
390:
391: ps = conn.prepareStatement(incrNextIDStmt);
392:
393: try {
394: nextIDMapping.setInt(null, ps, 1, nextHiValue + 1);
395: tableIDMapping.setInt(null, ps, 2, tableID);
396:
397: long startTime = System.currentTimeMillis();
398:
399: int rowsUpdated = ps.executeUpdate();
400:
401: if (LOG.isDebugEnabled())
402: LOG.debug("Time = "
403: + (System.currentTimeMillis() - startTime)
404: + " ms: " + incrNextIDStmt);
405:
406: if (rowsUpdated != 1)
407: throw new JDODataStoreException(
408: "Can't update table ID " + tableID + " in "
409: + getName() + "???");
410:
411: storeMgr.logSQLWarnings(ps);
412: } finally {
413: ps.close();
414: }
415:
416: return nextHiValue;
417: }
418:
419: public List getAllTableMetadata(boolean descending, Connection conn)
420: throws SQLException {
421: assertIsValidated();
422:
423: ArrayList tmds = new ArrayList();
424: Statement stmt = conn.createStatement();
425:
426: try {
427: String stmtText = fetchAllStmt
428: + (descending ? " DESC" : "");
429: long startTime = System.currentTimeMillis();
430:
431: ResultSet rs = stmt.executeQuery(stmtText);
432:
433: try {
434: if (LOG.isDebugEnabled())
435: LOG.debug("Time = "
436: + (System.currentTimeMillis() - startTime)
437: + " ms: " + stmtText);
438:
439: storeMgr.logSQLWarnings(stmt);
440:
441: while (rs.next()) {
442: int tableID = tableIDMapping.getInt(null, rs, 1);
443: String javaID = javaIDMapping
444: .getString(null, rs, 2);
445: SQLIdentifier tableName = new SQLIdentifier(dba,
446: tableNameMapping.getString(null, rs, 3));
447:
448: storeMgr.logSQLWarnings(rs);
449:
450: tmds.add(new TableMetadata(tableID, javaID,
451: tableName));
452: }
453: } finally {
454: rs.close();
455: }
456: } finally {
457: stmt.close();
458: }
459:
460: return tmds;
461: }
462:
463: public void dropTablesFor(Class[] classes, Connection conn)
464: throws SQLException {
465: assertIsValidated();
466:
467: String[] classNames = new String[classes.length];
468:
469: for (int i = 0; i < classes.length; ++i)
470: classNames[i] = classes[i].getName();
471:
472: ArrayList toBeRemoved = new ArrayList();
473: Iterator i = getAllTableMetadata(true, conn).iterator();
474:
475: while (i.hasNext()) {
476: TableMetadata tmd = (TableMetadata) i.next();
477:
478: for (int j = 0; j < classNames.length; ++j) {
479: String name = classNames[j];
480:
481: if (tmd.javaName.equals(name)
482: || tmd.javaName.startsWith(name + '.')) {
483: toBeRemoved.add(tmd);
484: break;
485: }
486: }
487: }
488:
489: dropTables(toBeRemoved, conn);
490: }
491:
492: public void dropAllTables(Connection conn) throws SQLException {
493: assertIsValidated();
494:
495: dropTables(getAllTableMetadata(true, conn), conn);
496: }
497:
498: private void dropTables(List metadata, Connection conn)
499: throws SQLException {
500: ArrayList allBaseTables = new ArrayList();
501: ArrayList allViews = new ArrayList();
502:
503: Iterator i = metadata.iterator();
504:
505: while (i.hasNext()) {
506: TableMetadata tmd = (TableMetadata) i.next();
507: int tableType = storeMgr.getTableType(tmd.tableName, conn);
508:
509: switch (tableType) {
510: case TABLE_TYPE_BASE_TABLE:
511: BaseTable t = new JDOBaseTable(tmd, storeMgr);
512: t.initialize();
513:
514: allBaseTables.add(t);
515: break;
516:
517: case TABLE_TYPE_VIEW:
518: View v = new JDOView(tmd, storeMgr);
519: v.initialize();
520:
521: allViews.add(v);
522: break;
523:
524: case TABLE_TYPE_MISSING:
525: case TABLE_TYPE_UNKNOWN:
526: default:
527: break;
528: }
529: }
530:
531: /*
532: * Tables, table constraints and views get removed in the reverse order
533: * from which they were created.
534: */
535: i = allViews.iterator();
536:
537: while (i.hasNext())
538: ((View) i.next()).drop(conn);
539:
540: i = allBaseTables.iterator();
541:
542: while (i.hasNext())
543: ((BaseTable) i.next()).dropConstraints(conn);
544:
545: i = allBaseTables.iterator();
546:
547: while (i.hasNext())
548: ((BaseTable) i.next()).drop(conn);
549: }
550: }
|