PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/tags/1.0rc1/core/src/main/java/com/orientechnologies/orient/core/sql/OCommandExecutorSQLUpdate.java

http://orient.googlecode.com/
Java | 438 lines | 310 code | 93 blank | 35 comment | 134 complexity | b840f2e34647f7ad5ace3ccda4c01c25 MD5 | raw file
Possible License(s): Apache-2.0, AGPL-3.0
  1. /*
  2. * Copyright 1999-2010 Luca Garulli (l.garulli--at--orientechnologies.com)
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.orientechnologies.orient.core.sql;
  17. import java.util.ArrayList;
  18. import java.util.Collection;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.Map;
  22. import java.util.Map.Entry;
  23. import com.orientechnologies.common.parser.OStringParser;
  24. import com.orientechnologies.common.util.OPair;
  25. import com.orientechnologies.orient.core.command.OCommandRequestText;
  26. import com.orientechnologies.orient.core.command.OCommandResultListener;
  27. import com.orientechnologies.orient.core.exception.OCommandExecutionException;
  28. import com.orientechnologies.orient.core.id.ORecordId;
  29. import com.orientechnologies.orient.core.metadata.schema.OProperty;
  30. import com.orientechnologies.orient.core.metadata.schema.OType;
  31. import com.orientechnologies.orient.core.metadata.security.ODatabaseSecurityResources;
  32. import com.orientechnologies.orient.core.metadata.security.ORole;
  33. import com.orientechnologies.orient.core.query.OQuery;
  34. import com.orientechnologies.orient.core.record.impl.ODocument;
  35. import com.orientechnologies.orient.core.sql.filter.OSQLFilterItem;
  36. import com.orientechnologies.orient.core.sql.functions.OSQLFunctionRuntime;
  37. import com.orientechnologies.orient.core.sql.query.OSQLAsynchQuery;
  38. /**
  39. * SQL UPDATE command.
  40. *
  41. * @author Luca Garulli
  42. *
  43. */
  44. public class OCommandExecutorSQLUpdate extends OCommandExecutorSQLAbstract implements OCommandResultListener {
  45. public static final String KEYWORD_UPDATE = "UPDATE";
  46. private static final String KEYWORD_SET = "SET";
  47. private static final String KEYWORD_ADD = "ADD";
  48. private static final String KEYWORD_PUT = "PUT";
  49. private static final String KEYWORD_REMOVE = "REMOVE";
  50. private Map<String, Object> setEntries = new HashMap<String, Object>();
  51. private Map<String, Object> addEntries = new HashMap<String, Object>();
  52. private Map<String, OPair<String, Object>> putEntries = new HashMap<String, OPair<String, Object>>();
  53. private Map<String, Object> removeEntries = new HashMap<String, Object>();
  54. private OQuery<?> query;
  55. private int recordCount = 0;
  56. private String subjectName;
  57. private static final Object EMPTY_VALUE = new Object();
  58. @SuppressWarnings("unchecked")
  59. public OCommandExecutorSQLUpdate parse(final OCommandRequestText iRequest) {
  60. iRequest.getDatabase().checkSecurity(ODatabaseSecurityResources.COMMAND, ORole.PERMISSION_UPDATE);
  61. init(iRequest.getDatabase(), iRequest.getText());
  62. setEntries.clear();
  63. query = null;
  64. recordCount = 0;
  65. final StringBuilder word = new StringBuilder();
  66. int pos = OSQLHelper.nextWord(text, textUpperCase, 0, word, true);
  67. if (pos == -1 || !word.toString().equals(OCommandExecutorSQLUpdate.KEYWORD_UPDATE))
  68. throw new OCommandSQLParsingException("Keyword " + OCommandExecutorSQLUpdate.KEYWORD_UPDATE + " not found", text, 0);
  69. int newPos = OSQLHelper.nextWord(text, textUpperCase, pos, word, true);
  70. if (newPos == -1)
  71. throw new OCommandSQLParsingException("Invalid target", text, pos);
  72. pos = newPos;
  73. subjectName = word.toString();
  74. newPos = OSQLHelper.nextWord(text, textUpperCase, pos, word, true);
  75. if (newPos == -1
  76. || (!word.toString().equals(KEYWORD_SET) && !word.toString().equals(KEYWORD_ADD) && !word.toString().equals(KEYWORD_PUT) && !word
  77. .toString().equals(KEYWORD_REMOVE)))
  78. throw new OCommandSQLParsingException("Expected keyword " + KEYWORD_SET + "," + KEYWORD_ADD + "," + KEYWORD_PUT + " or "
  79. + KEYWORD_REMOVE, text, pos);
  80. pos = newPos;
  81. while (pos != -1 && !word.toString().equals(OCommandExecutorSQLAbstract.KEYWORD_WHERE)) {
  82. if (word.toString().equals(KEYWORD_SET))
  83. pos = parseSetFields(word, pos);
  84. else if (word.toString().equals(KEYWORD_ADD))
  85. pos = parseAddFields(word, pos);
  86. else if (word.toString().equals(KEYWORD_PUT))
  87. pos = parsePutFields(word, pos);
  88. else if (word.toString().equals(KEYWORD_REMOVE))
  89. pos = parseRemoveFields(word, pos);
  90. else
  91. break;
  92. }
  93. String whereCondition = word.toString();
  94. if (whereCondition.equals(OCommandExecutorSQLAbstract.KEYWORD_WHERE))
  95. query = new OSQLAsynchQuery<ODocument>("select from " + subjectName + " where " + text.substring(pos), this);
  96. else
  97. query = new OSQLAsynchQuery<ODocument>("select from " + subjectName, this);
  98. return this;
  99. }
  100. public Object execute(final Map<Object, Object> iArgs) {
  101. if (subjectName == null)
  102. throw new OCommandExecutionException("Can't execute the command because it hasn't been parsed yet");
  103. database.query(query, iArgs);
  104. return recordCount;
  105. }
  106. /**
  107. * Update current record.
  108. */
  109. @SuppressWarnings("unchecked")
  110. public boolean result(final Object iRecord) {
  111. ODocument record = (ODocument) iRecord;
  112. boolean recordUpdated = false;
  113. // BIND VALUES TO UPDATE
  114. Object v;
  115. for (Map.Entry<String, Object> entry : setEntries.entrySet()) {
  116. v = entry.getValue();
  117. if (v instanceof OSQLFilterItem)
  118. v = ((OSQLFilterItem) v).getValue(record);
  119. record.field(entry.getKey(), v);
  120. recordUpdated = true;
  121. }
  122. // BIND VALUES TO ADD
  123. Collection<Object> coll;
  124. Object fieldValue;
  125. for (Map.Entry<String, Object> entry : addEntries.entrySet()) {
  126. coll = null;
  127. if (!record.containsField(entry.getKey())) {
  128. // GET THE TYPE IF ANY
  129. if (record.getSchemaClass() != null) {
  130. OProperty prop = record.getSchemaClass().getProperty(entry.getKey());
  131. if (prop != null && prop.getType() == OType.LINKSET)
  132. // SET TYPE
  133. coll = new HashSet<Object>();
  134. }
  135. if (coll == null)
  136. // IN ALL OTHER CASES USE A LIST
  137. coll = new ArrayList<Object>();
  138. record.field(entry.getKey(), coll);
  139. } else {
  140. fieldValue = record.field(entry.getKey());
  141. if (fieldValue instanceof Collection<?>)
  142. coll = (Collection<Object>) fieldValue;
  143. else
  144. continue;
  145. }
  146. v = entry.getValue();
  147. if (v instanceof OSQLFilterItem)
  148. v = ((OSQLFilterItem) v).getValue(record);
  149. else if (v instanceof OSQLFunctionRuntime)
  150. v = ((OSQLFunctionRuntime) v).execute(record);
  151. coll.add(v);
  152. recordUpdated = true;
  153. }
  154. // BIND VALUES TO PUT (AS MAP)
  155. Map<String, Object> map;
  156. OPair<String, Object> pair;
  157. for (Entry<String, OPair<String, Object>> entry : putEntries.entrySet()) {
  158. fieldValue = record.field(entry.getKey());
  159. if (fieldValue instanceof Map<?, ?>) {
  160. map = (Map<String, Object>) fieldValue;
  161. pair = entry.getValue();
  162. if (pair.getValue() instanceof OSQLFilterItem)
  163. pair.setValue(((OSQLFilterItem) pair.getValue()).getValue(record));
  164. else if (pair.getValue() instanceof OSQLFunctionRuntime)
  165. v = ((OSQLFunctionRuntime) pair.getValue()).execute(record);
  166. map.put(pair.getKey(), pair.getValue());
  167. recordUpdated = true;
  168. }
  169. }
  170. // REMOVE FIELD IF ANY
  171. for (Map.Entry<String, Object> entry : removeEntries.entrySet()) {
  172. v = entry.getValue();
  173. if (v == EMPTY_VALUE) {
  174. record.removeField(entry.getKey());
  175. recordUpdated = true;
  176. } else {
  177. fieldValue = record.field(entry.getKey());
  178. if (fieldValue instanceof Collection<?>) {
  179. coll = (Collection<Object>) fieldValue;
  180. if (coll.remove(v))
  181. recordUpdated = true;
  182. } else if (fieldValue instanceof Map<?, ?>) {
  183. map = (Map<String, Object>) fieldValue;
  184. if (map.remove(v) != null)
  185. recordUpdated = true;
  186. }
  187. }
  188. }
  189. if (recordUpdated) {
  190. record.setDirty();
  191. record.save();
  192. recordCount++;
  193. }
  194. return true;
  195. }
  196. private int parseSetFields(final StringBuilder word, int pos) {
  197. String fieldName;
  198. String fieldValue;
  199. int newPos = pos;
  200. while (pos != -1 && (setEntries.size() == 0 || word.toString().equals(","))) {
  201. newPos = OSQLHelper.nextWord(text, textUpperCase, pos, word, false);
  202. if (newPos == -1)
  203. throw new OCommandSQLParsingException("Field name expected", text, pos);
  204. pos = newPos;
  205. fieldName = word.toString();
  206. newPos = OStringParser.jumpWhiteSpaces(text, pos);
  207. if (newPos == -1 || text.charAt(newPos) != '=')
  208. throw new OCommandSQLParsingException("Character '=' was expected", text, pos);
  209. pos = newPos;
  210. newPos = OSQLHelper.nextWord(text, textUpperCase, pos + 1, word, false, " =><");
  211. if (pos == -1)
  212. throw new OCommandSQLParsingException("Value expected", text, pos);
  213. fieldValue = word.toString();
  214. if (fieldValue.endsWith(",")) {
  215. pos = newPos - 1;
  216. fieldValue = fieldValue.substring(0, fieldValue.length() - 1);
  217. } else
  218. pos = newPos;
  219. // INSERT TRANSFORMED FIELD VALUE
  220. setEntries.put(fieldName, OSQLHelper.parseValue(database, this, fieldValue));
  221. pos = OSQLHelper.nextWord(text, textUpperCase, pos, word, true);
  222. }
  223. if (setEntries.size() == 0)
  224. throw new OCommandSQLParsingException("Entries to set <field> = <value> are missed. Example: name = 'Bill', salary = 300.2",
  225. text, pos);
  226. return pos;
  227. }
  228. private int parseAddFields(final StringBuilder word, int pos) {
  229. String fieldName;
  230. String fieldValue;
  231. int newPos = pos;
  232. while (pos != -1 && (setEntries.size() == 0 || word.toString().equals(",")) && !word.toString().equals(KEYWORD_WHERE)) {
  233. newPos = OSQLHelper.nextWord(text, textUpperCase, pos, word, false);
  234. if (newPos == -1)
  235. throw new OCommandSQLParsingException("Field name expected", text, pos);
  236. pos = newPos;
  237. fieldName = word.toString();
  238. newPos = OStringParser.jumpWhiteSpaces(text, pos);
  239. if (newPos == -1 || text.charAt(newPos) != '=')
  240. throw new OCommandSQLParsingException("Character '=' was expected", text, pos);
  241. pos = newPos;
  242. newPos = OSQLHelper.nextWord(text, textUpperCase, pos + 1, word, false, " =><");
  243. if (pos == -1)
  244. throw new OCommandSQLParsingException("Value expected", text, pos);
  245. fieldValue = word.toString();
  246. if (fieldValue.endsWith(",")) {
  247. pos = newPos - 1;
  248. fieldValue = fieldValue.substring(0, fieldValue.length() - 1);
  249. } else
  250. pos = newPos;
  251. // INSERT TRANSFORMED FIELD VALUE
  252. addEntries.put(fieldName, OSQLHelper.parseValue(database, this, fieldValue));
  253. pos = OSQLHelper.nextWord(text, textUpperCase, pos, word, true);
  254. }
  255. if (addEntries.size() == 0)
  256. throw new OCommandSQLParsingException("Entries to add <field> = <value> are missed. Example: name = 'Bill', salary = 300.2",
  257. text, pos);
  258. return pos;
  259. }
  260. private int parsePutFields(final StringBuilder word, int pos) {
  261. String fieldName;
  262. String fieldKey;
  263. String fieldValue;
  264. int newPos = pos;
  265. while (pos != -1 && (setEntries.size() == 0 || word.toString().equals(",")) && !word.toString().equals(KEYWORD_WHERE)) {
  266. newPos = OSQLHelper.nextWord(text, textUpperCase, pos, word, false);
  267. if (newPos == -1)
  268. throw new OCommandSQLParsingException("Field name expected", text, pos);
  269. pos = newPos;
  270. fieldName = word.toString();
  271. newPos = OStringParser.jumpWhiteSpaces(text, pos);
  272. if (newPos == -1 || text.charAt(newPos) != '=')
  273. throw new OCommandSQLParsingException("Character '=' was expected", text, pos);
  274. pos = newPos;
  275. newPos = OSQLHelper.nextWord(text, textUpperCase, pos + 1, word, false, " =><,");
  276. if (pos == -1)
  277. throw new OCommandSQLParsingException("Key expected", text, pos);
  278. fieldKey = word.toString();
  279. if (fieldKey.endsWith(",")) {
  280. pos = newPos + 1;
  281. fieldKey = fieldKey.substring(0, fieldKey.length() - 1);
  282. } else {
  283. pos = newPos;
  284. newPos = OStringParser.jumpWhiteSpaces(text, pos);
  285. if (newPos == -1 || text.charAt(pos) != ',')
  286. throw new OCommandSQLParsingException("',' expected", text, pos);
  287. pos = newPos;
  288. }
  289. newPos = OSQLHelper.nextWord(text, textUpperCase, pos + 1, word, false, " =><,");
  290. if (pos == -1)
  291. throw new OCommandSQLParsingException("Value expected", text, pos);
  292. fieldValue = word.toString();
  293. if (fieldValue.endsWith(",")) {
  294. pos = newPos - 1;
  295. fieldValue = fieldValue.substring(0, fieldValue.length() - 1);
  296. } else
  297. pos = newPos;
  298. // INSERT TRANSFORMED FIELD VALUE
  299. putEntries.put(
  300. fieldName,
  301. new OPair<String, Object>((String) OSQLHelper.parseValue(database, this, fieldKey), OSQLHelper.parseValue(database, this,
  302. fieldValue)));
  303. pos = OSQLHelper.nextWord(text, textUpperCase, pos, word, true);
  304. }
  305. if (putEntries.size() == 0)
  306. throw new OCommandSQLParsingException("Entries to put <field> = <key>, <value> are missed. Example: name = 'Bill', 30", text,
  307. pos);
  308. return pos;
  309. }
  310. private int parseRemoveFields(final StringBuilder word, int pos) {
  311. String fieldName;
  312. String fieldValue;
  313. Object value;
  314. int newPos = pos;
  315. while (pos != -1 && (removeEntries.size() == 0 || word.toString().equals(",")) && !word.toString().equals(KEYWORD_WHERE)) {
  316. newPos = OSQLHelper.nextWord(text, textUpperCase, pos, word, false);
  317. if (newPos == -1)
  318. throw new OCommandSQLParsingException("Field name expected", text, pos);
  319. fieldName = word.toString();
  320. pos = OStringParser.jumpWhiteSpaces(text, newPos);
  321. if (pos > -1 && text.charAt(pos) == '=') {
  322. pos = OSQLHelper.nextWord(text, textUpperCase, pos + 1, word, false, " =><,");
  323. if (pos == -1)
  324. throw new OCommandSQLParsingException("Value expected", text, pos);
  325. fieldValue = word.toString();
  326. if (fieldValue.endsWith(",")) {
  327. pos = newPos - 1;
  328. fieldValue = fieldValue.substring(0, fieldValue.length() - 1);
  329. } else
  330. pos = newPos;
  331. if (fieldValue.length() > 2 && Character.isDigit(fieldValue.charAt(0)) && fieldValue.contains(":"))
  332. value = new ORecordId(fieldValue);
  333. else
  334. value = fieldValue;
  335. } else
  336. value = EMPTY_VALUE;
  337. // INSERT FIELD NAME TO BE REMOVED
  338. removeEntries.put(fieldName, value);
  339. pos = OSQLHelper.nextWord(text, textUpperCase, pos, word, true);
  340. }
  341. if (removeEntries.size() == 0)
  342. throw new OCommandSQLParsingException("Field(s) to remove are missed. Example: name, salary", text, pos);
  343. return pos;
  344. }
  345. }