001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.command.dml;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.command.Command;
011: import org.h2.command.Prepared;
012: import org.h2.constant.ErrorCode;
013: import org.h2.engine.Right;
014: import org.h2.engine.Session;
015: import org.h2.expression.Expression;
016: import org.h2.expression.Parameter;
017: import org.h2.index.Index;
018: import org.h2.log.UndoLogRecord;
019: import org.h2.message.Message;
020: import org.h2.result.LocalResult;
021: import org.h2.result.Row;
022: import org.h2.table.Column;
023: import org.h2.table.Table;
024: import org.h2.util.ObjectArray;
025: import org.h2.value.Value;
026:
027: /**
028: * This class represents the statement
029: * MERGE
030: */
031: public class Merge extends Prepared {
032:
033: private Table table;
034: private Column[] columns;
035: private Column[] keys;
036: private ObjectArray list = new ObjectArray();
037: private Query query;
038: private Prepared update;
039:
040: public Merge(Session session) {
041: super (session);
042: }
043:
044: public void setCommand(Command command) {
045: super .setCommand(command);
046: if (query != null) {
047: query.setCommand(command);
048: }
049: }
050:
051: public void setTable(Table table) {
052: this .table = table;
053: }
054:
055: public void setColumns(Column[] columns) {
056: this .columns = columns;
057: }
058:
059: public void setKeys(Column[] keys) {
060: this .keys = keys;
061: }
062:
063: public void setQuery(Query query) {
064: this .query = query;
065: }
066:
067: public void addRow(Expression[] expr) {
068: list.add(expr);
069: }
070:
071: public int update() throws SQLException {
072: int count;
073: session.getUser().checkRight(table, Right.INSERT);
074: session.getUser().checkRight(table, Right.UPDATE);
075: if (keys == null) {
076: Index idx = table.getPrimaryKey();
077: if (idx == null) {
078: throw Message
079: .getSQLException(
080: ErrorCode.CONSTRAINT_NOT_FOUND_1,
081: "PRIMARY KEY");
082: }
083: keys = idx.getColumns();
084: }
085: StringBuffer buff = new StringBuffer("UPDATE ");
086: buff.append(table.getSQL());
087: buff.append(" SET ");
088: for (int i = 0; i < columns.length; i++) {
089: if (i > 0) {
090: buff.append(", ");
091: }
092: buff.append(columns[i].getSQL());
093: buff.append("=?");
094: }
095: buff.append(" WHERE ");
096: for (int i = 0; i < keys.length; i++) {
097: if (i > 0) {
098: buff.append(" AND ");
099: }
100: buff.append(keys[i].getSQL());
101: buff.append("=?");
102: }
103: String sql = buff.toString();
104: update = session.prepare(sql);
105: setCurrentRowNumber(0);
106: if (list.size() > 0) {
107: count = 0;
108: for (int x = 0; x < list.size(); x++) {
109: setCurrentRowNumber(x + 1);
110: Expression[] expr = (Expression[]) list.get(x);
111: Row newRow = table.getTemplateRow();
112: for (int i = 0; i < columns.length; i++) {
113: Column c = columns[i];
114: int index = c.getColumnId();
115: Expression e = expr[i];
116: if (e != null) {
117: // e can be null (DEFAULT)
118: Value v = expr[i].getValue(session).convertTo(
119: c.getType());
120: newRow.setValue(index, v);
121: }
122: }
123: merge(newRow);
124: count++;
125: }
126: } else {
127: LocalResult rows = query.query(0);
128: count = 0;
129: table.fireBefore(session);
130: table.lock(session, true, false);
131: while (rows.next()) {
132: checkCancelled();
133: count++;
134: Value[] r = rows.currentRow();
135: Row newRow = table.getTemplateRow();
136: setCurrentRowNumber(count);
137: for (int j = 0; j < columns.length; j++) {
138: Column c = columns[j];
139: int index = c.getColumnId();
140: Value v = r[j].convertTo(c.getType());
141: newRow.setValue(index, v);
142: }
143: merge(newRow);
144: }
145: rows.close();
146: table.fireAfter(session);
147: }
148: return count;
149: }
150:
151: private void merge(Row row) throws SQLException {
152: ObjectArray k = update.getParameters();
153: for (int i = 0; i < columns.length; i++) {
154: Column col = columns[i];
155: Value v = row.getValue(col.getColumnId());
156: Parameter p = (Parameter) k.get(i);
157: p.setValue(v);
158: }
159: for (int i = 0; i < keys.length; i++) {
160: Column col = keys[i];
161: Value v = row.getValue(col.getColumnId());
162: if (v == null) {
163: throw Message.getSQLException(
164: ErrorCode.COLUMN_CONTAINS_NULL_VALUES_1, col
165: .getSQL());
166: }
167: Parameter p = (Parameter) k.get(columns.length + i);
168: p.setValue(v);
169: }
170: int count = update.update();
171: if (count == 0) {
172: table.fireBefore(session);
173: table.validateConvertUpdateSequence(session, row);
174: table.fireBeforeRow(session, null, row);
175: table.lock(session, true, false);
176: table.addRow(session, row);
177: session.log(table, UndoLogRecord.INSERT, row);
178: table.fireAfter(session);
179: table.fireAfterRow(session, null, row);
180: } else if (count != 1) {
181: throw Message.getSQLException(ErrorCode.DUPLICATE_KEY_1,
182: table.getSQL());
183: }
184: }
185:
186: public String getPlanSQL() {
187: StringBuffer buff = new StringBuffer();
188: buff.append("MERGE INTO ");
189: buff.append(table.getSQL());
190: buff.append('(');
191: for (int i = 0; i < columns.length; i++) {
192: if (i > 0) {
193: buff.append(", ");
194: }
195: buff.append(columns[i].getSQL());
196: }
197: buff.append(")");
198: if (keys != null) {
199: buff.append(" KEY(");
200: for (int i = 0; i < keys.length; i++) {
201: if (i > 0) {
202: buff.append(", ");
203: }
204: buff.append(keys[i].getSQL());
205: }
206: buff.append(")");
207: }
208: buff.append('\n');
209: if (list.size() > 0) {
210: buff.append("VALUES ");
211: for (int x = 0; x < list.size(); x++) {
212: Expression[] expr = (Expression[]) list.get(x);
213: if (x > 0) {
214: buff.append(", ");
215: }
216: buff.append("(");
217: for (int i = 0; i < columns.length; i++) {
218: if (i > 0) {
219: buff.append(", ");
220: }
221: Expression e = expr[i];
222: if (e == null) {
223: buff.append("DEFAULT");
224: } else {
225: buff.append(e.getSQL());
226: }
227: }
228: buff.append(')');
229: }
230: } else {
231: buff.append(query.getPlanSQL());
232: }
233: return buff.toString();
234: }
235:
236: public void prepare() throws SQLException {
237: if (columns == null) {
238: if (list.size() > 0
239: && ((Expression[]) list.get(0)).length == 0) {
240: // special case where table is used as a sequence
241: columns = new Column[0];
242: } else {
243: columns = table.getColumns();
244: }
245: }
246: if (list.size() > 0) {
247: for (int x = 0; x < list.size(); x++) {
248: Expression[] expr = (Expression[]) list.get(x);
249: if (expr.length != columns.length) {
250: throw Message
251: .getSQLException(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
252: }
253: for (int i = 0; i < expr.length; i++) {
254: Expression e = expr[i];
255: if (e != null) {
256: expr[i] = e.optimize(session);
257: }
258: }
259: }
260: } else {
261: query.prepare();
262: if (query.getColumnCount() != columns.length) {
263: throw Message
264: .getSQLException(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
265: }
266: }
267: }
268:
269: public boolean isTransactional() {
270: return true;
271: }
272:
273: public LocalResult queryMeta() {
274: return null;
275: }
276:
277: }
|