PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Source/facebook.Linq/Linq/QueryBuilder.cs

https://bitbucket.org/assaframan/facebooklinq
C# | 598 lines | 538 code | 46 blank | 14 comment | 90 complexity | 986dc4dc873ec1f6133834977ad4279b MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using System.Collections;
  8. using System.Diagnostics;
  9. namespace facebook.Linq
  10. {
  11. class FqlQueryBuilder
  12. {
  13. public FqlQueryBuilder(Expression query)
  14. {
  15. Query = query;
  16. QueryOptions = new QueryOptions();
  17. }
  18. public QueryOptions QueryOptions;
  19. public Expression Query;
  20. public string BuildQuery()
  21. {
  22. sb = new StringBuilder();
  23. Build(Query);
  24. var query = sb.ToString();
  25. return query;
  26. }
  27. #region Eval
  28. private object Eval(Expression exp)
  29. {
  30. switch (exp.NodeType)
  31. {
  32. case ExpressionType.MemberAccess:
  33. return Eval((MemberExpression)exp);
  34. case ExpressionType.Constant:
  35. return Eval((ConstantExpression)exp);
  36. default:
  37. throw new NotImplementedException();
  38. }
  39. }
  40. private object Eval(ConstantExpression exp)
  41. {
  42. return exp.Value;
  43. }
  44. private object Eval(MemberExpression exp)
  45. {
  46. var p = exp.Expression == null ? null : Eval(exp.Expression);
  47. if (exp.Member is PropertyInfo)
  48. {
  49. return ((PropertyInfo)exp.Member).GetValue(p, null);
  50. }
  51. else if (exp.Member is FieldInfo)
  52. {
  53. return ((FieldInfo)exp.Member).GetValue(p);
  54. }
  55. throw new NotImplementedException();
  56. }
  57. #endregion
  58. #region Build
  59. private void Build(object value)
  60. {
  61. if (value == null)
  62. throw new Exception("value is null");
  63. if (value is Expression)
  64. Build((Expression)value);
  65. else if (value is string)
  66. Build((string)value);
  67. else if (value is int)
  68. _Build((int)value);
  69. else if (value is long)
  70. _Build((long)value);
  71. else if (value is IFqlDataQuery)
  72. {
  73. var innerQuery = (IFqlDataQuery)value;
  74. var innerQueryText = innerQuery.ToString();
  75. if (InSelect > 0 && innerQueryText.StartsWith("from"))
  76. {
  77. Write(innerQueryText.Substring(4));
  78. }
  79. else
  80. {
  81. Write("(");
  82. Write(innerQueryText);//
  83. Write(")");
  84. }
  85. }
  86. else if (typeof(IEnumerable).IsAssignableFrom(value.GetType()))
  87. {
  88. var a = (IEnumerable)value;
  89. Write("(");
  90. bool first = true;
  91. foreach (var i in a)
  92. {
  93. if (first)
  94. first = false;
  95. else Write(",");
  96. Build(i);
  97. }
  98. Write(")");
  99. }
  100. else
  101. throw new NotImplementedException(value.GetType() + " is not a supported type");
  102. }
  103. private void Build(string literal)
  104. {
  105. sb.AppendFormat("'{0}' ", literal);
  106. }
  107. private void Build(IEnumerable<Expression> exps)
  108. {
  109. foreach (var exp in exps)
  110. {
  111. Build(exp);
  112. }
  113. }
  114. private void Build(Expression exp)
  115. {
  116. switch (exp.NodeType)
  117. {
  118. case ExpressionType.Call:
  119. _Build((MethodCallExpression)exp);
  120. break;
  121. case ExpressionType.Constant:
  122. _Build((ConstantExpression)exp);
  123. break;
  124. case ExpressionType.Quote:
  125. _Build((UnaryExpression)exp);
  126. break;
  127. case ExpressionType.Lambda:
  128. _Build((LambdaExpression)exp);
  129. break;
  130. case ExpressionType.NotEqual:
  131. case ExpressionType.Equal:
  132. _Build((BinaryExpression)exp);
  133. break;
  134. case ExpressionType.MemberAccess:
  135. _Build((MemberExpression)exp);
  136. break;
  137. case ExpressionType.Add:
  138. _Build((BinaryExpression)exp);
  139. break;
  140. case ExpressionType.New:
  141. _Build((NewExpression)exp);
  142. break;
  143. case ExpressionType.Convert:
  144. Build(((UnaryExpression)exp).Operand);
  145. break;
  146. case ExpressionType.AndAlso:
  147. case ExpressionType.OrElse:
  148. _Build(((BinaryExpression)exp));
  149. break;
  150. case ExpressionType.MemberInit:
  151. _Build(((MemberInitExpression)exp));
  152. break;
  153. case ExpressionType.AddChecked:
  154. case ExpressionType.And:
  155. case ExpressionType.ArrayIndex:
  156. case ExpressionType.ArrayLength:
  157. case ExpressionType.Coalesce:
  158. case ExpressionType.Conditional:
  159. case ExpressionType.ConvertChecked:
  160. case ExpressionType.Divide:
  161. case ExpressionType.ExclusiveOr:
  162. case ExpressionType.GreaterThan:
  163. case ExpressionType.GreaterThanOrEqual:
  164. case ExpressionType.Invoke:
  165. case ExpressionType.LeftShift:
  166. case ExpressionType.LessThan:
  167. case ExpressionType.LessThanOrEqual:
  168. case ExpressionType.ListInit:
  169. case ExpressionType.Modulo:
  170. case ExpressionType.Multiply:
  171. case ExpressionType.MultiplyChecked:
  172. case ExpressionType.Negate:
  173. case ExpressionType.NegateChecked:
  174. case ExpressionType.NewArrayBounds:
  175. case ExpressionType.NewArrayInit:
  176. case ExpressionType.Not:
  177. case ExpressionType.Or:
  178. case ExpressionType.Parameter:
  179. case ExpressionType.Power:
  180. case ExpressionType.RightShift:
  181. case ExpressionType.Subtract:
  182. case ExpressionType.SubtractChecked:
  183. case ExpressionType.TypeAs:
  184. case ExpressionType.TypeIs:
  185. case ExpressionType.UnaryPlus:
  186. default:
  187. throw new NotImplementedException(exp.NodeType + " is not supported");
  188. }
  189. }
  190. private void Build(MemberBinding b)
  191. {
  192. switch (b.BindingType)
  193. {
  194. case MemberBindingType.Assignment:
  195. {
  196. var ma = (MemberAssignment)b;
  197. Build(ma.Expression);
  198. }
  199. break;
  200. case MemberBindingType.ListBinding:
  201. throw new NotImplementedException();
  202. case MemberBindingType.MemberBinding:
  203. throw new NotImplementedException();
  204. }
  205. }
  206. private void _Build(MemberInitExpression exp)
  207. {
  208. var first = true;
  209. foreach (var b in exp.Bindings)
  210. {
  211. if (first)
  212. first = false;
  213. else Write(",");
  214. Build(b);
  215. }
  216. }
  217. private void _Build(MethodCallExpression exp)
  218. {
  219. if (exp.Method.Name == "Where")
  220. {
  221. bool nestedWhere = (IsMethodCall(exp.Arguments[0], "Where"));
  222. if (InSelect == 0 && !nestedWhere) //No projection has been specified, and we're in a Where clause (hence select * is assumed)
  223. {
  224. Write("from");
  225. }
  226. Build(exp.Arguments[0]);
  227. if (nestedWhere)
  228. {
  229. //Nested where
  230. Write("AND");
  231. }
  232. else
  233. {
  234. Write("where");
  235. }
  236. Build(exp.Arguments[1]);
  237. }
  238. else if (exp.Method.Name == "Select")
  239. {
  240. InSelect++;
  241. if (!HasSelect)
  242. {
  243. HasSelect = true;
  244. QueryOptions.Select = ((UnaryExpression)exp.Arguments[1]).Operand;
  245. }
  246. Write("select");
  247. Build(exp.Arguments[1]);
  248. Write("from");
  249. Build(exp.Arguments[0]);
  250. InSelect--;
  251. }
  252. else if (exp.Method.Name == "Contains")
  253. {
  254. Build(exp.Arguments[1]);
  255. Write("in");
  256. string innerQuery = new FqlQueryBuilder(exp.Arguments[0]).BuildQuery().Trim();
  257. if (!innerQuery.StartsWith("("))
  258. innerQuery = "(" + innerQuery + ")";
  259. Write(innerQuery);
  260. }
  261. else if (exp.Method.Name == "Take")
  262. {
  263. bool hasSkip = ((exp.Arguments[0] is MethodCallExpression) && (((((MethodCallExpression)(exp.Arguments[0])).Method)).Name == "Skip"));
  264. if (hasSkip)
  265. {
  266. Build((exp.Arguments[0] as MethodCallExpression).Arguments[0]);
  267. }
  268. else
  269. {
  270. Build(exp.Arguments[0]); // no skip specified
  271. }
  272. Write("limit ");
  273. Build(exp.Arguments[1]); //Take and Skip are used together, and in FQL / MySQL syntax
  274. if (hasSkip)
  275. {
  276. Write("offset ");
  277. Build((exp.Arguments[0] as MethodCallExpression).Arguments[1]);
  278. }
  279. }
  280. else if (exp.Method.Name == "Skip")
  281. {
  282. throw new Exception("Cannot use Skip without Take");
  283. //Build(exp.Arguments[0]);
  284. //Write("offset ");
  285. //Build(exp.Arguments[1]);
  286. }
  287. else if (exp.Method.Name == "First" || exp.Method.Name == "FirstOrDefault")
  288. {
  289. Build(exp.Arguments[0]);
  290. Write("limit 1");
  291. }
  292. else if (exp.Method.Name == "OrderBy")
  293. {
  294. Build(exp.Arguments[0]);
  295. Write("order by ");
  296. Build(exp.Arguments[1]);
  297. }
  298. else if (exp.Method.Name == "OrderByDescending")
  299. {
  300. Build(exp.Arguments[0]);
  301. Write("order by ");
  302. Build(exp.Arguments[1]);
  303. Write("DESC");
  304. }
  305. else if (exp.Method.Name == "ThenBy")
  306. {
  307. throw new NotImplementedException("ThenBy is not supported by FQL. Use a calculated key in the OrderBy clause");
  308. //Build(exp.Arguments[0]);
  309. //var desc = "";
  310. //if (sb.ToString().EndsWith("DESC "))
  311. //{
  312. // sb.Length -= 6;
  313. // desc = "DESC";
  314. //}
  315. //Write(",");
  316. //Build(exp.Arguments[1]);
  317. //sb.Append(desc);
  318. }
  319. else if (exp.Method.Name == "Count")
  320. {
  321. Build(exp.Arguments[0]);
  322. QueryOptions.IsCount = true;
  323. QueryOptions.Select = null; //No need to select when performing Count()
  324. if (sb.ToString().StartsWith("from"))
  325. {
  326. sb.Insert(0, "select '' ");
  327. }
  328. else if (sb.ToString().StartsWith("select"))
  329. {
  330. var s = sb.ToString();
  331. var idx = s.IndexOf("from");
  332. sb.Length = 0;
  333. sb.Append("select '' ");
  334. sb.Append(s.Substring(idx));
  335. }
  336. }
  337. else
  338. throw new Exception();
  339. }
  340. private void _Build(NewExpression exp)
  341. {
  342. bool first = true;
  343. foreach (var arg in exp.Arguments)
  344. {
  345. if (first)
  346. first = false;
  347. else
  348. Write(",");
  349. Build(arg);
  350. }
  351. }
  352. private void _Build(MemberExpression exp)
  353. {
  354. var member = exp.Member;
  355. var memberType = ReflectionHelper.GetMemberType(member);
  356. if (typeof(IFqlDataQuery).IsAssignableFrom(memberType)) //Query
  357. {
  358. if (exp.Expression is ConstantExpression)
  359. {
  360. var obj = ((ConstantExpression)(exp.Expression)).Value;
  361. var value = ReflectionHelper.GetMemberValue(member, obj);
  362. Build(value);
  363. }
  364. else
  365. throw new NotSupportedException();
  366. }
  367. else if (typeof(IFqlTable).IsAssignableFrom(memberType)) //Table
  368. {
  369. var tableRowType = ((PropertyInfo)exp.Member).PropertyType.GetGenericArguments()[0];
  370. var td = KnownTypeData.GetTypeData(tableRowType);
  371. var tblName = GetTableName(td);
  372. Write(tblName);
  373. }
  374. else //Column
  375. {
  376. var memberName = exp.Member.Name;
  377. var td = KnownTypeData.GetTypeData(exp.Member.DeclaringType);
  378. if (td.Properties.ContainsKey(memberName))
  379. {
  380. var pd = td.Properties[exp.Member.Name];
  381. Write(pd.FqlFieldName);
  382. }
  383. else //Variable
  384. {
  385. var value = Eval(exp);
  386. Build(value);
  387. }
  388. }
  389. }
  390. private void _Build(BinaryExpression exp)
  391. {
  392. Build(exp.Left);
  393. switch (exp.NodeType)
  394. {
  395. case ExpressionType.Add:
  396. Write("+");
  397. break;
  398. case ExpressionType.Subtract:
  399. Write("-");
  400. break;
  401. case ExpressionType.AndAlso:
  402. Write("AND");
  403. break;
  404. case ExpressionType.Equal:
  405. Write("=");
  406. break;
  407. case ExpressionType.OrElse:
  408. Write("OR");
  409. break;
  410. case ExpressionType.LessThan:
  411. Write("<");
  412. break;
  413. case ExpressionType.LessThanOrEqual:
  414. Write("<=");
  415. break;
  416. case ExpressionType.GreaterThan:
  417. Write(">");
  418. break;
  419. case ExpressionType.GreaterThanOrEqual:
  420. Write(">=");
  421. break;
  422. case ExpressionType.Modulo:
  423. Write("%");
  424. break;
  425. case ExpressionType.Multiply:
  426. Write("*");
  427. break;
  428. case ExpressionType.NotEqual:
  429. Write("<>");
  430. break;
  431. case ExpressionType.AddChecked:
  432. case ExpressionType.And:
  433. case ExpressionType.ArrayIndex:
  434. case ExpressionType.ArrayLength:
  435. case ExpressionType.Coalesce:
  436. case ExpressionType.Conditional:
  437. case ExpressionType.Convert:
  438. case ExpressionType.ConvertChecked:
  439. case ExpressionType.Divide:
  440. case ExpressionType.ExclusiveOr:
  441. case ExpressionType.Invoke:
  442. case ExpressionType.Lambda:
  443. case ExpressionType.LeftShift:
  444. case ExpressionType.ListInit:
  445. case ExpressionType.MultiplyChecked:
  446. case ExpressionType.Negate:
  447. case ExpressionType.NegateChecked:
  448. case ExpressionType.New:
  449. case ExpressionType.NewArrayBounds:
  450. case ExpressionType.NewArrayInit:
  451. case ExpressionType.Not:
  452. case ExpressionType.Or:
  453. case ExpressionType.Parameter:
  454. case ExpressionType.Power:
  455. case ExpressionType.Quote:
  456. case ExpressionType.RightShift:
  457. case ExpressionType.SubtractChecked:
  458. case ExpressionType.TypeAs:
  459. case ExpressionType.TypeIs:
  460. case ExpressionType.UnaryPlus:
  461. default:
  462. throw new NotImplementedException();
  463. }
  464. Build(exp.Right);
  465. }
  466. private void _Build(LambdaExpression exp)
  467. {
  468. Build(exp.Body);
  469. }
  470. private void _Build(UnaryExpression exp)
  471. {
  472. Build(exp.Operand);
  473. }
  474. private void _Build(ConstantExpression exp)
  475. {
  476. var value = exp.Value;
  477. var type = value.GetType();
  478. if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(FqlTable<>))
  479. {
  480. var elemType = EnumerableHelper.GetEnumerableItemType(type);
  481. var tblName = GetTableName(KnownTypeData.GetTypeData(elemType));
  482. Write("{0}", tblName);
  483. }
  484. else
  485. Build(exp.Value);
  486. }
  487. #endregion
  488. #region Numbers
  489. private void _Build(int p)
  490. {
  491. Write(p.ToString());
  492. }
  493. private void _Build(uint p)
  494. {
  495. Write(p.ToString());
  496. }
  497. private void _Build(long p)
  498. {
  499. Write(p.ToString());
  500. }
  501. private void _Build(ulong p)
  502. {
  503. Write(p.ToString());
  504. }
  505. private void _Build(float p)
  506. {
  507. Write(p.ToString());
  508. }
  509. private void _Build(double p)
  510. {
  511. Write(p.ToString());
  512. }
  513. private void _Build(byte p)
  514. {
  515. Write(p.ToString());
  516. }
  517. private void _Build(sbyte p)
  518. {
  519. Write(p.ToString());
  520. }
  521. private void _Build(short p)
  522. {
  523. Write(p.ToString());
  524. }
  525. private void _Build(ushort p)
  526. {
  527. Write(p.ToString());
  528. }
  529. #endregion
  530. #region Utils
  531. bool HasSelect;
  532. int InSelect = 0;
  533. bool IsMethodCall(Expression exp, string methodName)
  534. {
  535. return (exp != null && exp.NodeType == ExpressionType.Call && ((MethodCallExpression)exp).Method.Name == methodName);
  536. }
  537. void Write(string format, params object[] args)
  538. {
  539. sb.AppendFormat(format, args);
  540. sb.Append(" ");
  541. }
  542. private string GetTableName(TypeData td)
  543. {
  544. var tblName = td.FqlTableName;
  545. if (td.Type.Assembly != GetType().Assembly && td.Type.Assembly!=typeof(facebook.Schema.user).Assembly)
  546. tblName = "app." + tblName;
  547. return tblName;
  548. }
  549. StringBuilder sb;
  550. #endregion
  551. }
  552. }