EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.command.dml]

COVERAGE SUMMARY FOR SOURCE FILE [Merge.java]

nameclass, %method, %block, %line, %
Merge.java100% (1/1)93%  (14/15)91%  (700/767)91%  (154.8/170)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Merge100% (1/1)93%  (14/15)91%  (700/767)91%  (154.8/170)
queryMeta (): ResultInterface 0%   (0/1)0%   (0/2)0%   (0/1)
merge (Row): void 100% (1/1)90%  (163/182)92%  (36.8/40)
getPlanSQL (): String 100% (1/1)91%  (151/166)91%  (29/32)
update (): int 100% (1/1)91%  (170/186)91%  (39/43)
prepare (): void 100% (1/1)92%  (170/185)88%  (30/34)
Merge (Session): void 100% (1/1)100% (7/7)100% (3/3)
addRow (Expression []): void 100% (1/1)100% (6/6)100% (2/2)
getType (): int 100% (1/1)100% (2/2)100% (1/1)
isCacheable (): boolean 100% (1/1)100% (2/2)100% (1/1)
isTransactional (): boolean 100% (1/1)100% (2/2)100% (1/1)
setColumns (Column []): void 100% (1/1)100% (4/4)100% (2/2)
setCommand (Command): void 100% (1/1)100% (11/11)100% (4/4)
setKeys (Column []): void 100% (1/1)100% (4/4)100% (2/2)
setQuery (Query): void 100% (1/1)100% (4/4)100% (2/2)
setTable (Table): void 100% (1/1)100% (4/4)100% (2/2)

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

[all classes][org.h2.command.dml]
EMMA 2.0.5312 (C) Vladimir Roubtsov