001: /**
002: * com.mckoi.database.interpret.Insert 13 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 java.util.*;
026: import com.mckoi.database.*;
027: import com.mckoi.database.sql.ParseException;
028: import com.mckoi.util.IntegerVector;
029:
030: /**
031: * The instance class that stores all the information about an insert
032: * statement for processing.
033: *
034: * @author Tobias Downer
035: */
036:
037: public class Insert extends Statement {
038:
039: String table_name;
040:
041: ArrayList col_list;
042:
043: ArrayList values_list; //list contains List of elements to insert
044:
045: StatementTree select;
046:
047: ArrayList column_sets;
048:
049: boolean from_values = false;
050:
051: boolean from_select = false;
052:
053: boolean from_set = false;
054:
055: // -----
056:
057: /**
058: * The table we are inserting stuff to.
059: */
060: private DataTable insert_table;
061:
062: /**
063: * For 'from_values' and 'from_select', this is a list of indices into the
064: * 'insert_table' for the columns that we are inserting data into.
065: */
066: private int[] col_index_list;
067:
068: /**
069: * The list of Variable objects the represent the list of columns being
070: * inserted into in this query.
071: */
072: private Variable[] col_var_list;
073:
074: /**
075: * The TableName we are inserting into.
076: */
077: private TableName tname;
078:
079: /**
080: * If this is a 'from_select' insert, the prepared Select object.
081: */
082: private Select prepared_select;
083:
084: /**
085: * Tables that are relationally linked to the table being inserted into, set
086: * after 'prepare'. This is used to determine the tables we need to read
087: * lock because we need to validate relational constraints on the tables.
088: */
089: private ArrayList relationally_linked_tables;
090:
091: // ---------- Implemented from Statement ----------
092:
093: public void prepare() throws DatabaseException {
094:
095: // Prepare this object from the StatementTree
096: table_name = (String) cmd.getObject("table_name");
097: col_list = (ArrayList) cmd.getObject("col_list");
098: values_list = (ArrayList) cmd.getObject("data_list");
099: select = (StatementTree) cmd.getObject("select");
100: column_sets = (ArrayList) cmd.getObject("assignments");
101: String type = (String) cmd.getObject("type");
102: from_values = type.equals("from_values");
103: from_select = type.equals("from_select");
104: from_set = type.equals("from_set");
105:
106: // ---
107:
108: // Check 'values_list' contains all same size size insert element arrays.
109: int first_len = -1;
110: for (int n = 0; n < values_list.size(); ++n) {
111: List exp_list = (List) values_list.get(n);
112: if (first_len == -1 || first_len == exp_list.size()) {
113: first_len = exp_list.size();
114: } else {
115: throw new DatabaseException(
116: "The insert data list varies in size.");
117: }
118: }
119:
120: tname = resolveTableName(table_name, database);
121:
122: // Does the table exist?
123: if (!database.tableExists(tname)) {
124: throw new DatabaseException("Table '" + tname
125: + "' does not exist.");
126: }
127:
128: // Add the from table direct source for this table
129: TableQueryDef table_query_def = database.getTableQueryDef(
130: tname, null);
131: addTable(new FromTableDirectSource(database, table_query_def,
132: "INSERT_TABLE", tname, tname));
133:
134: // Get the table we are inserting to
135: insert_table = database.getTable(tname);
136:
137: // If column list is empty, then fill it with all columns from table.
138: if (from_values || from_select) {
139: // If 'col_list' is empty we must pick every entry from the insert
140: // table.
141: if (col_list.size() == 0) {
142: for (int i = 0; i < insert_table.getColumnCount(); ++i) {
143: col_list.add(insert_table.getColumnDefAt(i)
144: .getName());
145: }
146: }
147: // Resolve 'col_list' into a list of column indices into the insert
148: // table.
149: col_index_list = new int[col_list.size()];
150: col_var_list = new Variable[col_list.size()];
151: for (int i = 0; i < col_list.size(); ++i) {
152: // Variable col = Variable.resolve(tname, (String) col_list.get(i));
153: Variable in_var = Variable.resolve((String) col_list
154: .get(i));
155: Variable col = resolveColumn(in_var);
156: int index = insert_table.fastFindFieldName(col);
157: if (index == -1) {
158: throw new DatabaseException("Can't find column: "
159: + col);
160: }
161: col_index_list[i] = index;
162: col_var_list[i] = col;
163: }
164: }
165:
166: // Make the 'from_values' clause into a 'from_set'
167: if (from_values) {
168:
169: // If values to insert is different from columns list,
170: if (col_list.size() != ((List) values_list.get(0)).size()) {
171: throw new DatabaseException(
172: "Number of columns to insert is "
173: + "different from columns selected to insert to.");
174: }
175:
176: // Resolve all expressions in the added list.
177: // For each value
178: for (int i = 0; i < values_list.size(); ++i) {
179: // Each value is a list of either expressions or "DEFAULT"
180: List insert_elements = (List) values_list.get(i);
181: int sz = insert_elements.size();
182: for (int n = 0; n < sz; ++n) {
183: Object elem = insert_elements.get(n);
184: if (elem instanceof Expression) {
185: Expression exp = (Expression) elem;
186: List elem_list = exp.allElements();
187: for (int p = 0; p < elem_list.size(); ++p) {
188: Object ob = elem_list.get(p);
189: if (ob instanceof Select) {
190: throw new DatabaseException(
191: "Illegal to have sub-select in expression.");
192: }
193: }
194: // Resolve the expression.
195: resolveExpression(exp);
196: }
197: }
198: }
199:
200: } else if (from_select) {
201: // Prepare the select statement
202: prepared_select = new Select();
203: prepared_select.init(database, select, null);
204: prepared_select.prepare();
205: }
206:
207: // If from a set, then resolve all values,
208: else if (from_set) {
209:
210: // If there's a sub select in an expression in the 'SET' clause then
211: // throw an error.
212: for (int i = 0; i < column_sets.size(); ++i) {
213: Assignment assignment = (Assignment) column_sets.get(i);
214: Expression exp = assignment.getExpression();
215: List elem_list = exp.allElements();
216: for (int n = 0; n < elem_list.size(); ++n) {
217: Object ob = elem_list.get(n);
218: if (ob instanceof Select) {
219: throw new DatabaseException(
220: "Illegal to have sub-select in SET clause.");
221: }
222: }
223:
224: // Resolve the column names in the columns set.
225: Variable v = assignment.getVariable();
226: Variable resolved_v = resolveVariableName(v);
227: v.set(resolved_v);
228: resolveExpression(assignment.getExpression());
229: }
230:
231: }
232:
233: // Resolve all tables linked to this
234: TableName[] linked_tables = database
235: .queryTablesRelationallyLinkedTo(tname);
236: relationally_linked_tables = new ArrayList(linked_tables.length);
237: for (int i = 0; i < linked_tables.length; ++i) {
238: relationally_linked_tables.add(database
239: .getTable(linked_tables[i]));
240: }
241:
242: }
243:
244: public Table evaluate() throws DatabaseException {
245:
246: DatabaseQueryContext context = new DatabaseQueryContext(database);
247:
248: // Check that this user has privs to insert into the table.
249: if (!database.getDatabase().canUserInsertIntoTableObject(
250: context, user, tname, col_var_list)) {
251: throw new UserAccessException(
252: "User not permitted to insert in to table: " + table_name);
253: }
254:
255: // Are we inserting from a select statement or from a 'set' assignment
256: // list?
257: int insert_count = 0;
258:
259: if (from_values) {
260: // Set each row from the VALUES table,
261: for (int i = 0; i < values_list.size(); ++i) {
262: List insert_elements = (List) values_list.get(i);
263: RowData row_data = insert_table.createRowDataObject(context);
264: row_data.setupEntire(col_index_list, insert_elements, context);
265: insert_table.add(row_data);
266: ++insert_count;
267: }
268: }
269: else if (from_select) {
270: // Insert rows from the result select table.
271: Table result = prepared_select.evaluate();
272: if (result.getColumnCount() != col_index_list.length) {
273: throw new DatabaseException(
274: "Number of columns in result don't match columns to insert.");
275: }
276:
277: // Copy row list into an intermediate IntegerVector list.
278: // (A RowEnumeration for a table being modified is undefined).
279: IntegerVector row_list = new IntegerVector();
280: RowEnumeration enum = result.rowEnumeration();
281: while (enum.hasMoreRows()) {
282: row_list.addInt(enum.nextRowIndex());
283: }
284:
285: // For each row of the select table.
286: int sz = row_list.size();
287: for (int i = 0; i < sz; ++i) {
288: int rindex = row_list.intAt(i);
289: RowData row_data = insert_table.createRowDataObject(context);
290: for (int n = 0; n < col_index_list.length; ++n) {
291: TObject cell = result.getCellContents(n, rindex);
292: row_data.setColumnData(col_index_list[n], cell);
293: }
294: row_data.setDefaultForRest(context);
295: insert_table.add(row_data);
296: ++insert_count;
297: }
298: }
299: else if (from_set) {
300: // Insert rows from the set assignments.
301: RowData row_data = insert_table.createRowDataObject(context);
302: Assignment[] assignments = (Assignment[])
303: column_sets.toArray(new Assignment[column_sets.size()]);
304: row_data.setupEntire(assignments, context);
305: insert_table.add(row_data);
306: ++insert_count;
307: }
308:
309: // Notify TriggerManager that we've just done an update.
310: if (insert_count > 0) {
311: database.notifyTriggerEvent(new TriggerEvent(
312: TriggerEvent.INSERT, tname.toString(), insert_count));
313: }
314:
315: // Return the number of rows we inserted.
316: return FunctionTable.resultTable(context, insert_count);
317: }
318: }
|