001: /**
002: * com.mckoi.database.interpret.ColumnChecker 09 Sep 2001
003: *
004: * Mckoi SQL Database ( http://www.mckoi.com/database )
005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * Version 2 as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License Version 2 for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * Version 2 along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: * Change Log:
021: *
022: *
023: */package com.mckoi.database.interpret;
024:
025: import com.mckoi.database.*;
026: import java.util.ArrayList;
027: import java.util.List;
028:
029: /**
030: * A class that abstracts the checking of information in a table. This is
031: * abstracted because the behaviour is shared between ALTER and CREATE
032: * statement.
033: *
034: * @author Tobias Downer
035: */
036:
037: abstract class ColumnChecker {
038:
039: /**
040: * Given a column name string, this will strip off the preceeding table
041: * name if there is one specified. For example, 'Customer.id' would
042: * become 'id'. This also checks that the table specification is in the
043: * given table domain. For example,
044: * stripTableName("Customer", "Customer.id") would not throw an error but
045: * stripTableName("Order", "Customer.di") would.
046: */
047: static String stripTableName(String table_domain, String column) {
048: if (column.indexOf('.') != -1) {
049: String st = table_domain + ".";
050: if (!column.startsWith(st)) {
051: throw new StatementException("Column '" + column
052: + "' is not within the expected table domain '"
053: + table_domain + "'");
054: }
055: column = column.substring(st.length());
056: }
057: return column;
058: }
059:
060: /**
061: * Calls the 'stripTableName' method on all elements in the given list.
062: */
063: static ArrayList stripColumnList(String table_domain,
064: ArrayList column_list) {
065: if (column_list != null) {
066: int size = column_list.size();
067: for (int i = 0; i < size; ++i) {
068: String res = stripTableName(table_domain,
069: (String) column_list.get(i));
070: column_list.set(i, res);
071: }
072: }
073: return column_list;
074: }
075:
076: /**
077: * Returns the resolved column name if the column exists within the table
078: * being checked under, or null if it doesn't. Throws an error if the
079: * column name is abiguous.
080: */
081: abstract String resolveColumnName(String col_name)
082: throws DatabaseException;
083:
084: /**
085: * Resolves all the variables in the expression throwing a DatabaseException
086: * if any errors found. This checks that all variables point to a column
087: * in the table being created.
088: */
089: void checkExpression(Expression expression)
090: throws DatabaseException {
091:
092: if (expression != null) {
093: List list = expression.allVariables();
094: for (int i = 0; i < list.size(); ++i) {
095: Variable v = (Variable) list.get(i);
096: String orig_col = v.getName();
097: String resolved_column = resolveColumnName(orig_col);
098: if (resolved_column == null) {
099: throw new DatabaseException("Column '" + orig_col
100: + "' not found in the table.");
101: }
102: // Resolve the column name
103: if (!orig_col.equals(resolved_column)) {
104: v.setColumnName(resolved_column);
105: }
106:
107: }
108:
109: // Don't allow select statements because they don't convert to a
110: // text string that we can encode into the DataTableDef file.
111: if (expression.hasSubQuery()) {
112: throw new DatabaseException(
113: "Sub-queries not permitted in "
114: + "the check constraint expression.");
115: }
116: }
117:
118: }
119:
120: /**
121: * Checks all the columns in the list and throws an exception if any
122: * column names are not found in the columns in this create. Additionally
123: * sets the entry with the correct column resolved to.
124: */
125: void checkColumnList(ArrayList list) throws DatabaseException {
126: if (list != null) {
127: for (int i = 0; i < list.size(); ++i) {
128: String col = (String) list.get(i);
129: String resolved_col = resolveColumnName(col);
130: if (resolved_col == null) {
131: throw new DatabaseException("Column '" + col
132: + "' not found the table.");
133: }
134: list.set(i, resolved_col);
135: }
136: }
137: }
138:
139: // ---------- Statics ----------
140:
141: /**
142: * Given a DatabaseConnection and a TableName object, this returns an
143: * implementation of ColumnChecker that is able to check that the column
144: * name exists in the table, and that the reference is not ambigious.
145: */
146: static ColumnChecker standardColumnChecker(
147: DatabaseConnection database, TableName tname) {
148: final DataTableDef table_def = database.getTable(tname)
149: .getDataTableDef();
150: final boolean ignores_case = database.isInCaseInsensitiveMode();
151:
152: // Implement the checker
153: return new ColumnChecker() {
154: String resolveColumnName(String col_name)
155: throws DatabaseException {
156: // We need to do case sensitive and case insensitive resolution,
157: String found_col = null;
158: for (int n = 0; n < table_def.columnCount(); ++n) {
159: DataTableColumnDef col = (DataTableColumnDef) table_def
160: .columnAt(n);
161: if (!ignores_case) {
162: if (col.getName().equals(col_name)) {
163: return col_name;
164: }
165: } else {
166: if (col.getName().equalsIgnoreCase(col_name)) {
167: if (found_col != null) {
168: throw new DatabaseException(
169: "Ambiguous column name '"
170: + col_name + "'");
171: }
172: found_col = col.getName();
173: }
174: }
175: }
176: return found_col;
177: }
178: };
179: }
180:
181: }
|