1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.hbql.statement;
22
23 import org.apache.expreval.expr.TypeSupport;
24 import org.apache.expreval.expr.function.DelegateFunction;
25 import org.apache.expreval.expr.literal.DefaultKeyword;
26 import org.apache.expreval.expr.literal.NullLiteral;
27 import org.apache.expreval.expr.node.GenericValue;
28 import org.apache.expreval.expr.var.DelegateColumn;
29 import org.apache.hadoop.hbase.hbql.client.ExecutionResults;
30 import org.apache.hadoop.hbase.hbql.client.HBatch;
31 import org.apache.hadoop.hbase.hbql.client.HBqlException;
32 import org.apache.hadoop.hbase.hbql.client.HRecord;
33 import org.apache.hadoop.hbase.hbql.impl.HConnectionImpl;
34 import org.apache.hadoop.hbase.hbql.impl.InvalidTypeException;
35 import org.apache.hadoop.hbase.hbql.mapping.ColumnAttrib;
36 import org.apache.hadoop.hbase.hbql.statement.args.InsertValueSource;
37 import org.apache.hadoop.hbase.hbql.statement.select.SelectExpressionContext;
38 import org.apache.hadoop.hbase.hbql.util.Lists;
39
40 import java.util.List;
41
42 public class InsertStatement extends StatementWithParameters implements ConnectionStatement {
43
44 private final List<SelectExpressionContext> columnList = Lists.newArrayList();
45 private final InsertValueSource insertValuesSource;
46
47 private HConnectionImpl connection = null;
48 private boolean validated = false;
49 private HRecord record = null;
50 private String invalidInsertColumn = null;
51
52 public InsertStatement(final StatementPredicate predicate,
53 final String mappingName,
54 final List<GenericValue> columnList,
55 final InsertValueSource insertValuesSource) {
56 super(predicate, mappingName);
57
58 for (final GenericValue val : columnList) {
59
60 if (val instanceof DelegateFunction) {
61 final DelegateFunction function = (DelegateFunction)val;
62 final String familyName = function.getFunctionName();
63 for (final GenericValue columnarg : function.getGenericValueList()) {
64 if (columnarg instanceof DelegateColumn) {
65 final String columnName = ((DelegateColumn)columnarg).getVariableName();
66 final DelegateColumn col = new DelegateColumn(familyName + ":" + columnName);
67 this.getInsertColumnList().add(SelectExpressionContext.newExpression(col, null));
68 }
69 else {
70
71 if (invalidInsertColumn == null)
72 invalidInsertColumn = columnarg.asString();
73 }
74 }
75 }
76 else {
77 this.getInsertColumnList().add(SelectExpressionContext.newExpression(val, null));
78 }
79 }
80
81 this.insertValuesSource = insertValuesSource;
82 this.getInsertValuesSource().setInsertStatement(this);
83 }
84
85 private boolean isValidated() {
86 return this.validated;
87 }
88
89 private HRecord getHRecord() {
90 return this.record;
91 }
92
93 public HConnectionImpl getConnection() {
94 return this.connection;
95 }
96
97 private List<SelectExpressionContext> getInsertColumnList() {
98 return this.columnList;
99 }
100
101 private InsertValueSource getInsertValuesSource() {
102 return this.insertValuesSource;
103 }
104
105 public void validate(final HConnectionImpl conn) throws HBqlException {
106
107 if (this.isValidated())
108 return;
109 else
110 this.validated = true;
111
112 if (this.invalidInsertColumn != null)
113 throw new InvalidTypeException(this.invalidInsertColumn + " is not a column reference in " + this.asString());
114
115 this.connection = conn;
116 this.getMappingContext().validateMappingName(this.getConnection());
117 this.record = this.getConnection().getMapping(this.getMappingContext().getMappingName()).newHRecord();
118
119 for (final SelectExpressionContext element : this.getInsertColumnList()) {
120
121 element.validate(this.getMappingContext(), this.getConnection());
122
123 if (!element.isADelegateColumnReference())
124 throw new InvalidTypeException(element.asString() + " is not a column reference in " + this.asString());
125 }
126
127 if (!this.hasAKeyValue())
128 throw new InvalidTypeException("Missing a key value in attribute list in " + this.asString());
129
130 this.getInsertValuesSource().validate();
131
132 this.collectParameters();
133 }
134
135 public void validateTypes() throws HBqlException {
136
137 final List<Class<? extends GenericValue>> columnsTypeList = this.getColumnsTypeList();
138 final List<Class<? extends GenericValue>> valuesTypeList = this.getInsertValuesSource().getValuesTypeList();
139
140 if (columnsTypeList.size() != valuesTypeList.size())
141 throw new HBqlException("Number of columns not equal to number of values in " + this.asString());
142
143 for (int i = 0; i < columnsTypeList.size(); i++) {
144
145 final Class<? extends GenericValue> type1 = columnsTypeList.get(i);
146 final Class<? extends GenericValue> type2 = valuesTypeList.get(i);
147
148
149 if (type2 == DefaultKeyword.class) {
150 final String name = this.getInsertColumnList().get(i).asString();
151 final ColumnAttrib attrib = this.getMappingContext().getMapping().getAttribByVariableName(name);
152 if (!attrib.hasDefaultArg())
153 throw new HBqlException("No DEFAULT value specified for " + attrib.getNameToUseInExceptions()
154 + " in " + this.asString());
155 continue;
156 }
157
158 if (type2.equals(NullLiteral.class)) {
159 if (!TypeSupport.allowsNullValues(type1))
160 throw new InvalidTypeException("Argument " + i
161 + " type " + type1.getSimpleName()
162 + " cannot be assigned a NULL value"
163 + " in " + this.asString());
164 continue;
165 }
166
167 if (!TypeSupport.isParentClass(type1, type2))
168 throw new InvalidTypeException("Type mismatch with argument " + i
169 + " expecting " + type1.getSimpleName()
170 + " but found " + type2.getSimpleName()
171 + " in " + this.asString());
172 }
173 }
174
175 private List<Class<? extends GenericValue>> getColumnsTypeList() throws HBqlException {
176 final List<Class<? extends GenericValue>> typeList = Lists.newArrayList();
177 for (final SelectExpressionContext element : this.getInsertColumnList()) {
178 final Class<? extends GenericValue> type = element.getExpressionType();
179 typeList.add(type);
180 }
181 return typeList;
182 }
183
184 private boolean hasAKeyValue() {
185 for (final SelectExpressionContext element : this.getInsertColumnList()) {
186 if (element.isAKeyValue())
187 return true;
188 }
189 return false;
190 }
191
192 private void collectParameters() {
193 this.getNamedParameters().addParameters(this.getInsertValuesSource().getParameterList());
194 }
195
196 public void resetParameters() {
197 this.getInsertValuesSource().reset();
198 this.getHRecord().reset();
199 }
200
201 public int setStatementParameter(final String name, final Object val) throws HBqlException {
202 final int cnt = this.getInsertValuesSource().setInsertSourceParameter(name, val);
203 if (cnt == 0)
204 throw new HBqlException("Parameter name " + name + " does not exist in " + this.asString());
205 return cnt;
206 }
207
208 protected ExecutionResults execute(final HConnectionImpl conn) throws HBqlException {
209
210 this.validate(conn);
211
212 this.validateTypes();
213
214 int cnt = 0;
215
216 this.getInsertValuesSource().execute();
217
218 while (this.getInsertValuesSource().hasValues()) {
219
220 final HBatch<HRecord> batch = conn.newHBatch();
221
222 for (int i = 0; i < this.getInsertColumnList().size(); i++) {
223 final String name = this.getInsertColumnList().get(i).asString();
224 final Object val;
225 if (this.getInsertValuesSource().isDefaultValue(i)) {
226 final ColumnAttrib attrib = this.getMappingContext().getMapping().getAttribByVariableName(name);
227 val = attrib.getDefaultValue();
228 }
229 else {
230 val = this.getInsertValuesSource().getValue(conn, i);
231 }
232 this.getHRecord().setCurrentValue(name, val);
233 }
234
235 batch.insert(this.getHRecord());
236
237 batch.apply();
238 cnt++;
239 }
240
241 final ExecutionResults results = new ExecutionResults(cnt + " record" + ((cnt > 1) ? "s" : "") + " inserted");
242 results.setCount(cnt);
243 return results;
244 }
245
246 public String asString() {
247
248 final StringBuilder sbuf = new StringBuilder();
249
250 sbuf.append("INSERT INTO ");
251 sbuf.append(this.getMappingContext().getMappingName());
252 sbuf.append(" (");
253
254 boolean firstTime = true;
255 for (final SelectExpressionContext val : this.getInsertColumnList()) {
256 if (!firstTime)
257 sbuf.append(", ");
258 firstTime = false;
259
260 sbuf.append(val.asString());
261 }
262
263 sbuf.append(") ");
264 sbuf.append(this.getInsertValuesSource().asString());
265 return sbuf.toString();
266 }
267
268 public static String usage() {
269 return "INSERT INTO [MAPPING] mapping_name (column_name_list) insert_values [IF bool_expr]";
270 }
271 }