PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/referencesource/System.Data.Linq/SqlClient/Query/SqlNamer.cs

https://github.com/pruiz/mono
C# | 270 lines | 224 code | 38 blank | 8 comment | 41 complexity | 581738ec29995cee72c47171f098f455 MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Data.Linq;
  5. using System.Diagnostics.CodeAnalysis;
  6. namespace System.Data.Linq.SqlClient {
  7. internal class SqlNamer {
  8. Visitor visitor;
  9. internal SqlNamer() {
  10. this.visitor = new Visitor();
  11. }
  12. internal SqlNode AssignNames(SqlNode node) {
  13. return this.visitor.Visit(node);
  14. }
  15. class Visitor : SqlVisitor {
  16. int aliasCount;
  17. SqlAlias alias;
  18. bool makeUnique;
  19. bool useMappedNames;
  20. string lastName;
  21. internal Visitor() {
  22. this.makeUnique = true;
  23. this.useMappedNames = false;
  24. }
  25. internal string GetNextAlias() {
  26. return "t" + (aliasCount++);
  27. }
  28. internal override SqlAlias VisitAlias(SqlAlias sqlAlias) {
  29. SqlAlias save = this.alias;
  30. this.alias = sqlAlias;
  31. sqlAlias.Node = this.Visit(sqlAlias.Node);
  32. sqlAlias.Name = this.GetNextAlias();
  33. this.alias = save;
  34. return sqlAlias;
  35. }
  36. internal override SqlExpression VisitScalarSubSelect(SqlSubSelect ss) {
  37. base.VisitScalarSubSelect(ss);
  38. if (ss.Select.Row.Columns.Count > 0) {
  39. System.Diagnostics.Debug.Assert(ss != null && ss.Select != null && ss.Select.Row != null && ss.Select.Row.Columns.Count == 1);
  40. // make sure these scalar subselects don't get redundantly named
  41. ss.Select.Row.Columns[0].Name = "";
  42. }
  43. return ss;
  44. }
  45. internal override SqlStatement VisitInsert(SqlInsert insert) {
  46. bool saveMakeUnique = this.makeUnique;
  47. this.makeUnique = false;
  48. bool saveUseMappedNames = this.useMappedNames;
  49. this.useMappedNames = true;
  50. SqlStatement stmt = base.VisitInsert(insert);
  51. this.makeUnique = saveMakeUnique;
  52. this.useMappedNames = saveUseMappedNames;
  53. return stmt;
  54. }
  55. internal override SqlStatement VisitUpdate(SqlUpdate update) {
  56. bool saveMakeUnique = this.makeUnique;
  57. this.makeUnique = false;
  58. bool saveUseMappedNames = this.useMappedNames;
  59. this.useMappedNames = true;
  60. SqlStatement stmt = base.VisitUpdate(update);
  61. this.makeUnique = saveMakeUnique;
  62. this.useMappedNames = saveUseMappedNames;
  63. return stmt;
  64. }
  65. internal override SqlSelect VisitSelect(SqlSelect select) {
  66. select = base.VisitSelect(select);
  67. string[] names = new string[select.Row.Columns.Count];
  68. for (int i = 0, n = names.Length; i < n; i++) {
  69. SqlColumn c = select.Row.Columns[i];
  70. string name = c.Name;
  71. if (name == null) {
  72. name = SqlNamer.DiscoverName(c);
  73. }
  74. names[i] = name;
  75. c.Name = null;
  76. }
  77. var reservedNames = this.GetColumnNames(select.OrderBy);
  78. for (int i = 0, n = select.Row.Columns.Count; i < n; i++) {
  79. SqlColumn c = select.Row.Columns[i];
  80. string rootName = names[i];
  81. string name = rootName;
  82. if (this.makeUnique) {
  83. int iName = 1;
  84. while (!this.IsUniqueName(select.Row.Columns, reservedNames, c, name)) {
  85. iName++;
  86. name = rootName + iName;
  87. }
  88. }
  89. c.Name = name;
  90. c.Ordinal = i;
  91. }
  92. return select;
  93. }
  94. [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
  95. private bool IsUniqueName(List<SqlColumn> columns, ICollection<string> reservedNames, SqlColumn c, string name) {
  96. foreach (SqlColumn sc in columns) {
  97. if (sc != c && string.Compare(sc.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
  98. return false;
  99. }
  100. if (!IsSimpleColumn(c, name)) {
  101. return !reservedNames.Contains(name);
  102. }
  103. return true;
  104. }
  105. /// <summary>
  106. /// An expression is a simple reprojection if it's a column node whose expression is null, or
  107. /// whose expression is a column whose name matches the name of the given name or where
  108. /// where the given name is null or empty.
  109. /// </summary>
  110. /// <param name="c"></param>
  111. /// <returns></returns>
  112. private static bool IsSimpleColumn(SqlColumn c, string name) {
  113. if (c.Expression != null) {
  114. switch (c.Expression.NodeType) {
  115. case SqlNodeType.ColumnRef:
  116. var colRef = c.Expression as SqlColumnRef;
  117. return String.IsNullOrEmpty(name) || string.Compare(name, colRef.Column.Name, StringComparison.OrdinalIgnoreCase) == 0;
  118. default:
  119. return false;
  120. }
  121. }
  122. return true;
  123. }
  124. internal override SqlExpression VisitExpression(SqlExpression expr) {
  125. string saveLastName = this.lastName;
  126. this.lastName = null;
  127. try {
  128. return (SqlExpression)this.Visit(expr);
  129. }
  130. finally {
  131. this.lastName = saveLastName;
  132. }
  133. }
  134. private SqlExpression VisitNamedExpression(SqlExpression expr, string name) {
  135. string saveLastName = this.lastName;
  136. this.lastName = name;
  137. try {
  138. return (SqlExpression)this.Visit(expr);
  139. }
  140. finally {
  141. this.lastName = saveLastName;
  142. }
  143. }
  144. internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
  145. if (cref.Column.Name == null && this.lastName != null) {
  146. cref.Column.Name = this.lastName;
  147. }
  148. return cref;
  149. }
  150. internal override SqlExpression VisitNew(SqlNew sox) {
  151. if (sox.Constructor != null) {
  152. System.Reflection.ParameterInfo[] pis = sox.Constructor.GetParameters();
  153. for (int i = 0, n = sox.Args.Count; i < n; i++) {
  154. sox.Args[i] = this.VisitNamedExpression(sox.Args[i], pis[i].Name);
  155. }
  156. }
  157. else {
  158. for (int i = 0, n = sox.Args.Count; i < n; i++) {
  159. sox.Args[i] = this.VisitExpression(sox.Args[i]);
  160. }
  161. }
  162. foreach (SqlMemberAssign ma in sox.Members) {
  163. string n = ma.Member.Name;
  164. if (this.useMappedNames) {
  165. n = sox.MetaType.GetDataMember(ma.Member).MappedName;
  166. }
  167. ma.Expression = this.VisitNamedExpression(ma.Expression, n);
  168. }
  169. return sox;
  170. }
  171. internal override SqlExpression VisitGrouping(SqlGrouping g) {
  172. g.Key = this.VisitNamedExpression(g.Key, "Key");
  173. g.Group = this.VisitNamedExpression(g.Group, "Group");
  174. return g;
  175. }
  176. internal override SqlExpression VisitOptionalValue(SqlOptionalValue sov) {
  177. sov.HasValue = this.VisitNamedExpression(sov.HasValue, "test");
  178. sov.Value = this.VisitExpression(sov.Value);
  179. return sov;
  180. }
  181. internal override SqlExpression VisitMethodCall(SqlMethodCall mc) {
  182. mc.Object = this.VisitExpression(mc.Object);
  183. System.Reflection.ParameterInfo[] pis = mc.Method.GetParameters();
  184. for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
  185. mc.Arguments[i] = this.VisitNamedExpression(mc.Arguments[i], pis[i].Name);
  186. }
  187. return mc;
  188. }
  189. ICollection<string> GetColumnNames(IEnumerable<SqlOrderExpression> orderList)
  190. {
  191. var visitor = new ColumnNameGatherer();
  192. foreach (var expr in orderList) {
  193. visitor.Visit(expr.Expression);
  194. }
  195. return visitor.Names;
  196. }
  197. }
  198. internal static string DiscoverName(SqlExpression e) {
  199. if (e != null) {
  200. switch (e.NodeType) {
  201. case SqlNodeType.Column:
  202. return DiscoverName(((SqlColumn)e).Expression);
  203. case SqlNodeType.ColumnRef:
  204. SqlColumnRef cref = (SqlColumnRef)e;
  205. if (cref.Column.Name != null) return cref.Column.Name;
  206. return DiscoverName(cref.Column);
  207. case SqlNodeType.ExprSet:
  208. SqlExprSet eset = (SqlExprSet)e;
  209. return DiscoverName(eset.Expressions[0]);
  210. }
  211. }
  212. return "value";
  213. }
  214. class ColumnNameGatherer : SqlVisitor {
  215. public HashSet<string> Names { get; set; }
  216. public ColumnNameGatherer()
  217. : base() {
  218. this.Names = new HashSet<string>();
  219. }
  220. internal override SqlExpression VisitColumn(SqlColumn col) {
  221. if (!String.IsNullOrEmpty(col.Name)) {
  222. this.Names.Add(col.Name);
  223. }
  224. return base.VisitColumn(col);
  225. }
  226. internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
  227. Visit(cref.Column);
  228. return base.VisitColumnRef(cref);
  229. }
  230. }
  231. }
  232. }