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

COVERAGE SUMMARY FOR SOURCE FILE [Replace.java]

nameclass, %method, %block, %line, %
Replace.java100% (1/1)0%   (0/16)0%   (0/784)0%   (0/174)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Replace100% (1/1)0%   (0/16)0%   (0/784)0%   (0/174)
Replace (Session): void 0%   (0/1)0%   (0/7)0%   (0/3)
addRow (Expression []): void 0%   (0/1)0%   (0/6)0%   (0/2)
getPlanSQL (): String 0%   (0/1)0%   (0/127)0%   (0/25)
getType (): int 0%   (0/1)0%   (0/2)0%   (0/1)
isCacheable (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
isTransactional (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
prepare (): void 0%   (0/1)0%   (0/232)0%   (0/42)
queryMeta (): ResultInterface 0%   (0/1)0%   (0/2)0%   (0/1)
replace (Row): void 0%   (0/1)0%   (0/113)0%   (0/27)
setColumns (Column []): void 0%   (0/1)0%   (0/4)0%   (0/2)
setCommand (Command): void 0%   (0/1)0%   (0/11)0%   (0/4)
setKeys (Column []): void 0%   (0/1)0%   (0/4)0%   (0/2)
setQuery (Query): void 0%   (0/1)0%   (0/4)0%   (0/2)
setTable (Table): void 0%   (0/1)0%   (0/4)0%   (0/2)
update (): int 0%   (0/1)0%   (0/186)0%   (0/43)
update (Row): int 0%   (0/1)0%   (0/78)0%   (0/16)

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 org.h2.api.ErrorCode;
9import org.h2.api.Trigger;
10import org.h2.command.Command;
11import org.h2.command.CommandInterface;
12import org.h2.command.Prepared;
13import org.h2.engine.Right;
14import org.h2.engine.Session;
15import org.h2.engine.UndoLogRecord;
16import org.h2.expression.Expression;
17import org.h2.expression.Parameter;
18import org.h2.index.Index;
19import org.h2.message.DbException;
20import org.h2.result.ResultInterface;
21import org.h2.result.Row;
22import org.h2.table.Column;
23import org.h2.table.Table;
24import org.h2.util.New;
25import org.h2.util.StatementBuilder;
26import org.h2.value.Value;
27 
28import java.util.ArrayList;
29 
30/**
31 * This class represents the MySQL-compatibility REPLACE statement
32 */
33public class Replace extends Prepared {
34 
35    private Table table;
36    private Column[] columns;
37    private Column[] keys;
38    private final ArrayList<Expression[]> list = New.arrayList();
39    private Query query;
40    private Prepared update;
41 
42    public Replace(Session session) {
43        super(session);
44    }
45 
46    @Override
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 replace statement.
72     *
73     * @param expr the list of values
74     */
75    public void addRow(Expression[] expr) {
76        list.add(expr);
77    }
78 
79    @Override
80    public int update() {
81        int count;
82        session.getUser().checkRight(table, Right.INSERT);
83        session.getUser().checkRight(table, Right.UPDATE);
84        setCurrentRowNumber(0);
85        if (list.size() > 0) {
86            count = 0;
87            for (int x = 0, size = list.size(); x < size; x++) {
88                setCurrentRowNumber(x + 1);
89                Expression[] expr = list.get(x);
90                Row newRow = table.getTemplateRow();
91                for (int i = 0, len = columns.length; i < len; i++) {
92                    Column c = columns[i];
93                    int index = c.getColumnId();
94                    Expression e = expr[i];
95                    if (e != null) {
96                        // e can be null (DEFAULT)
97                        try {
98                            Value v = c.convert(e.getValue(session));
99                            newRow.setValue(index, v);
100                        } catch (DbException ex) {
101                            throw setRow(ex, count, getSQL(expr));
102                        }
103                    }
104                }
105                replace(newRow);
106                count++;
107            }
108        } else {
109            ResultInterface rows = query.query(0);
110            count = 0;
111            table.fire(session, Trigger.UPDATE | Trigger.INSERT, true);
112            table.lock(session, true, false);
113            while (rows.next()) {
114                count++;
115                Value[] r = rows.currentRow();
116                Row newRow = table.getTemplateRow();
117                setCurrentRowNumber(count);
118                for (int j = 0; j < columns.length; j++) {
119                    Column c = columns[j];
120                    int index = c.getColumnId();
121                    try {
122                        Value v = c.convert(r[j]);
123                        newRow.setValue(index, v);
124                    } catch (DbException ex) {
125                        throw setRow(ex, count, getSQL(r));
126                    }
127                }
128                replace(newRow);
129            }
130            rows.close();
131            table.fire(session, Trigger.UPDATE | Trigger.INSERT, false);
132        }
133        return count;
134    }
135 
136    private void replace(Row row) {
137        int count = update(row);
138        if (count == 0) {
139            try {
140                table.validateConvertUpdateSequence(session, row);
141                boolean done = table.fireBeforeRow(session, null, row);
142                if (!done) {
143                    table.lock(session, true, false);
144                    table.addRow(session, row);
145                    session.log(table, UndoLogRecord.INSERT, row);
146                    table.fireAfterRow(session, null, row, false);
147                }
148            } catch (DbException e) {
149                if (e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
150                    // possibly a concurrent replace or insert
151                    Index index = (Index) e.getSource();
152                    if (index != null) {
153                        // verify the index columns match the key
154                        Column[] indexColumns = index.getColumns();
155                        boolean indexMatchesKeys = false;
156                        if (indexColumns.length <= keys.length) {
157                            for (int i = 0; i < indexColumns.length; i++) {
158                                if (indexColumns[i] != keys[i]) {
159                                    indexMatchesKeys = false;
160                                    break;
161                                }
162                            }
163                        }
164                        if (indexMatchesKeys) {
165                            throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName());
166                        }
167                    }
168                }
169                throw e;
170            }
171        } else if (count != 1) {
172            throw DbException.get(ErrorCode.DUPLICATE_KEY_1, table.getSQL());
173        }
174    }
175 
176    private int update(Row row) {
177        // if there is no valid primary key,
178        // the statement degenerates to an INSERT
179        if (update == null) {
180            return 0;
181        }
182        ArrayList<Parameter> k = update.getParameters();
183        for (int i = 0; i < columns.length; i++) {
184            Column col = columns[i];
185            Value v = row.getValue(col.getColumnId());
186            Parameter p = k.get(i);
187            p.setValue(v);
188        }
189        for (int i = 0; i < keys.length; i++) {
190            Column col = keys[i];
191            Value v = row.getValue(col.getColumnId());
192            if (v == null) {
193                throw DbException.get(ErrorCode.COLUMN_CONTAINS_NULL_VALUES_1, col.getSQL());
194            }
195            Parameter p = k.get(columns.length + i);
196            p.setValue(v);
197        }
198        return update.update();
199    }
200 
201    @Override
202    public String getPlanSQL() {
203        StatementBuilder buff = new StatementBuilder("REPLACE INTO ");
204        buff.append(table.getSQL()).append('(');
205        for (Column c : columns) {
206            buff.appendExceptFirst(", ");
207            buff.append(c.getSQL());
208        }
209        buff.append(')');
210        buff.append('\n');
211        if (list.size() > 0) {
212            buff.append("VALUES ");
213            int row = 0;
214            for (Expression[] expr : list) {
215                if (row++ > 0) {
216                    buff.append(", ");
217                }
218                buff.append('(');
219                buff.resetCount();
220                for (Expression e : expr) {
221                    buff.appendExceptFirst(", ");
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    @Override
237    public void prepare() {
238        if (columns == null) {
239            if (list.size() > 0 && 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 (Expression[] expr : list) {
248                if (expr.length != columns.length) {
249                    throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
250                }
251                for (int i = 0; i < expr.length; i++) {
252                    Expression e = expr[i];
253                    if (e != null) {
254                        expr[i] = e.optimize(session);
255                    }
256                }
257            }
258        } else {
259            query.prepare();
260            if (query.getColumnCount() != columns.length) {
261                throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
262            }
263        }
264        if (keys == null) {
265            Index idx = table.getPrimaryKey();
266            if (idx == null) {
267                throw DbException.get(ErrorCode.CONSTRAINT_NOT_FOUND_1, "PRIMARY KEY");
268            }
269            keys = idx.getColumns();
270        }
271        // if there is no valid primary key, the statement degenerates to an
272        // INSERT
273        for (Column key : keys) {
274            boolean found = false;
275            for (Column column : columns) {
276                if (column.getColumnId() == key.getColumnId()) {
277                    found = true;
278                    break;
279                }
280            }
281            if (!found) {
282                return;
283            }
284        }
285        StatementBuilder buff = new StatementBuilder("UPDATE ");
286        buff.append(table.getSQL()).append(" SET ");
287        for (Column c : columns) {
288            buff.appendExceptFirst(", ");
289            buff.append(c.getSQL()).append("=?");
290        }
291        buff.append(" WHERE ");
292        buff.resetCount();
293        for (Column c : keys) {
294            buff.appendExceptFirst(" AND ");
295            buff.append(c.getSQL()).append("=?");
296        }
297        String sql = buff.toString();
298        update = session.prepare(sql);
299    }
300 
301    @Override
302    public boolean isTransactional() {
303        return true;
304    }
305 
306    @Override
307    public ResultInterface queryMeta() {
308        return null;
309    }
310 
311    @Override
312    public int getType() {
313        return CommandInterface.REPLACE;
314    }
315 
316    @Override
317    public boolean isCacheable() {
318        return true;
319    }
320 
321}

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