File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\command\dml\Merge.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.command.dml;
8
9 import java.util.ArrayList;
10 import org.h2.api.Trigger;
11 import org.h2.command.Command;
12 import org.h2.command.CommandInterface;
13 import org.h2.command.Prepared;
14 import org.h2.constant.ErrorCode;
15 import org.h2.engine.Right;
16 import org.h2.engine.Session;
17 import org.h2.engine.UndoLogRecord;
18 import org.h2.expression.Expression;
19 import org.h2.expression.Parameter;
20 import org.h2.index.Index;
21 import org.h2.message.DbException;
22 import org.h2.result.ResultInterface;
23 import org.h2.result.Row;
24 import org.h2.table.Column;
25 import org.h2.table.Table;
26 import org.h2.util.New;
27 import org.h2.util.StatementBuilder;
28 import org.h2.value.Value;
29
30 /**
31 * This class represents the statement
32 * MERGE
33 */
34 public class Merge extends Prepared {
35
36 private Table table;
37 private Column[] columns;
38 private Column[] keys;
39 private final ArrayList<Expression[]> list = New.arrayList();
40 private Query query;
41 private Prepared update;
42
43 public Merge(Session session) {
44 super(session);
45 }
46
47 public void setCommand(Command command) {
48 super.setCommand(command);
49 if (query != null) {
50 query.setCommand(command);
51 }
52 }
53
54 public void setTable(Table table) {
55 this.table = table;
56 }
57
58 public void setColumns(Column[] columns) {
59 this.columns = columns;
60 }
61
62 public void setKeys(Column[] keys) {
63 this.keys = keys;
64 }
65
66 public void setQuery(Query query) {
67 this.query = query;
68 }
69
70 /**
71 * Add a row to this merge statement.
72 *
73 * @param expr the list of values
74 */
75 public void addRow(Expression[] expr) {
76 list.add(expr);
77 }
78
79 public int update() {
80 int count;
81 session.getUser().checkRight(table, Right.INSERT);
82 session.getUser().checkRight(table, Right.UPDATE);
83 setCurrentRowNumber(0);
84 if (list.size() > 0) {
85 count = 0;
86 for (int x = 0, size = list.size(); x < size; x++) {
87 setCurrentRowNumber(x + 1);
88 Expression[] expr = list.get(x);
89 Row newRow = table.getTemplateRow();
90 for (int i = 0, len = columns.length; i < len; i++) {
91 Column c = columns[i];
92 int index = c.getColumnId();
93 Expression e = expr[i];
94 if (e != null) {
95 // e can be null (DEFAULT)
96 try {
97 Value v = c.convert(e.getValue(session));
98 newRow.setValue(index, v);
99 } catch (DbException ex) {
100 throw setRow(ex, count, getSQL(expr));
101 }
102 }
103 }
104 merge(newRow);
105 count++;
106 }
107 } else {
108 ResultInterface rows = query.query(0);
109 count = 0;
110 table.fire(session, Trigger.UPDATE | Trigger.INSERT, true);
111 table.lock(session, true, false);
112 while (rows.next()) {
113 count++;
114 Value[] r = rows.currentRow();
115 Row newRow = table.getTemplateRow();
116 setCurrentRowNumber(count);
117 for (int j = 0; j < columns.length; j++) {
118 Column c = columns[j];
119 int index = c.getColumnId();
120 try {
121 Value v = c.convert(r[j]);
122 newRow.setValue(index, v);
123 } catch (DbException ex) {
124 throw setRow(ex, count, getSQL(r));
125 }
126 }
127 merge(newRow);
128 }
129 rows.close();
130 table.fire(session, Trigger.UPDATE | Trigger.INSERT, false);
131 }
132 return count;
133 }
134
135 private void merge(Row row) {
136 ArrayList<Parameter> k = update.getParameters();
137 for (int i = 0; i < columns.length; i++) {
138 Column col = columns[i];
139 Value v = row.getValue(col.getColumnId());
140 Parameter p = k.get(i);
141 p.setValue(v);
142 }
143 for (int i = 0; i < keys.length; i++) {
144 Column col = keys[i];
145 Value v = row.getValue(col.getColumnId());
146 if (v == null) {
147 throw DbException.get(ErrorCode.COLUMN_CONTAINS_NULL_VALUES_1, col.getSQL());
148 }
149 Parameter p = k.get(columns.length + i);
150 p.setValue(v);
151 }
152 int count = update.update();
153 if (count == 0) {
154 try {
155 table.validateConvertUpdateSequence(session, row);
156 boolean done = table.fireBeforeRow(session, null, row);
157 if (!done) {
158 table.lock(session, true, false);
159 table.addRow(session, row);
160 session.log(table, UndoLogRecord.INSERT, row);
161 table.fireAfterRow(session, null, row, false);
162 }
163 } catch (DbException e) {
164 if (e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
165 // possibly a concurrent merge or insert
166 Index index = (Index) e.getSource();
167 if (index != null) {
168 // verify the index columns match the key
169 Column[] indexColumns = index.getColumns();
170 boolean indexMatchesKeys = false;
171 if (indexColumns.length <= keys.length) {
172 for (int i = 0; i < indexColumns.length; i++) {
173 if (indexColumns[i] != keys[i]) {
174 indexMatchesKeys = false;
175 break;
176 }
177 }
178 }
179 if (indexMatchesKeys) {
180 throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName());
181 }
182 }
183 }
184 throw e;
185 }
186 } else if (count != 1) {
187 throw DbException.get(ErrorCode.DUPLICATE_KEY_1, table.getSQL());
188 }
189 }
190
191 public String getPlanSQL() {
192 StatementBuilder buff = new StatementBuilder("MERGE INTO ");
193 buff.append(table.getSQL()).append('(');
194 for (Column c : columns) {
195 buff.appendExceptFirst(", ");
196 buff.append(c.getSQL());
197 }
198 buff.append(')');
199 if (keys != null) {
200 buff.append(" KEY(");
201 buff.resetCount();
202 for (Column c : keys) {
203 buff.appendExceptFirst(", ");
204 buff.append(c.getSQL());
205 }
206 buff.append(')');
207 }
208 buff.append('\n');
209 if (list.size() > 0) {
210 buff.append("VALUES ");
211 int row = 0;
212 for (Expression[] expr : list) {
213 if (row++ > 0) {
214 buff.append(", ");
215 }
216 buff.append('(');
217 buff.resetCount();
218 for (Expression e : expr) {
219 buff.appendExceptFirst(", ");
220 if (e == null) {
221 buff.append("DEFAULT");
222 } else {
223 buff.append(e.getSQL());
224 }
225 }
226 buff.append(')');
227 }
228 } else {
229 buff.append(query.getPlanSQL());
230 }
231 return buff.toString();
232 }
233
234 public void prepare() {
235 if (columns == null) {
236 if (list.size() > 0 && list.get(0).length == 0) {
237 // special case where table is used as a sequence
238 columns = new Column[0];
239 } else {
240 columns = table.getColumns();
241 }
242 }
243 if (list.size() > 0) {
244 for (Expression[] expr : list) {
245 if (expr.length != columns.length) {
246 throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
247 }
248 for (int i = 0; i < expr.length; i++) {
249 Expression e = expr[i];
250 if (e != null) {
251 expr[i] = e.optimize(session);
252 }
253 }
254 }
255 } else {
256 query.prepare();
257 if (query.getColumnCount() != columns.length) {
258 throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
259 }
260 }
261 if (keys == null) {
262 Index idx = table.getPrimaryKey();
263 if (idx == null) {
264 throw DbException.get(ErrorCode.CONSTRAINT_NOT_FOUND_1, "PRIMARY KEY");
265 }
266 keys = idx.getColumns();
267 }
268 StatementBuilder buff = new StatementBuilder("UPDATE ");
269 buff.append(table.getSQL()).append(" SET ");
270 for (Column c : columns) {
271 buff.appendExceptFirst(", ");
272 buff.append(c.getSQL()).append("=?");
273 }
274 buff.append(" WHERE ");
275 buff.resetCount();
276 for (Column c : keys) {
277 buff.appendExceptFirst(" AND ");
278 buff.append(c.getSQL()).append("=?");
279 }
280 String sql = buff.toString();
281 update = session.prepare(sql);
282 }
283
284 public boolean isTransactional() {
285 return true;
286 }
287
288 public ResultInterface queryMeta() {
289 return null;
290 }
291
292 public int getType() {
293 return CommandInterface.MERGE;
294 }
295
296 public boolean isCacheable() {
297 return true;
298 }
299
300 }
301