internal override SqlExpression VisitUserColumn(SqlUserColumn suc)
		{
			SqlExpression result = base.VisitUserColumn(suc);
			if(result == suc)
			{ // must be external
				return ExtractParameter(result);
			}
			return result;
		}
Beispiel #2
0
 internal virtual SqlExpression VisitUserColumn(SqlUserColumn suc) {
     return suc;
 }
Beispiel #3
0
		private IObjectReaderFactory GetDefaultFactory(MetaType rowType)
		{
			if(rowType == null)
			{
				throw Error.ArgumentNull("rowType");
			}
			SqlNodeAnnotations annotations = new SqlNodeAnnotations();
			Expression tmp = Expression.Constant(null);
			SqlUserQuery suq = new SqlUserQuery(string.Empty, null, null, tmp);
			if(TypeSystem.IsSimpleType(rowType.Type))
			{
				// if the element type is a simple type (int, bool, etc.) we create
				// a single column binding
				SqlUserColumn col = new SqlUserColumn(rowType.Type, _typeProvider.From(rowType.Type), suq, "", false, suq.SourceExpression);
				suq.Columns.Add(col);
				suq.Projection = col;
			}
			else
			{
				// ... otherwise we generate a default projection
				SqlUserRow rowExp = new SqlUserRow(rowType.InheritanceRoot, _typeProvider.GetApplicationType((int)ConverterSpecialTypes.Row), suq, tmp);
				suq.Projection = _translator.BuildProjection(rowExp, rowType, true, null, tmp);
			}
			Type resultType = TypeSystem.GetSequenceType(rowType.Type);
			QueryInfo[] qis = this.BuildQuery(ResultShape.Sequence, resultType, suq, null, annotations);
			return this.GetReaderFactory(qis[qis.Length - 1].Query, rowType.Type);
		}
		private SqlUserQuery VisitUserQuery(string query, Expression[] arguments, Type resultType)
		{
			SqlExpression[] args = new SqlExpression[arguments.Length];
			for(int i = 0, n = args.Length; i < n; i++)
			{
				args[i] = this.VisitExpression(arguments[i]);
			}
			SqlUserQuery suq = new SqlUserQuery(query, null, args, _dominatingExpression);
			if(resultType != typeof(void))
			{
				Type elementType = TypeSystem.GetElementType(resultType);
				MetaType mType = _services.Model.GetMetaType(elementType);

				// if the element type is a simple type (int, bool, etc.) we create
				// a single column binding
				if(TypeSystem.IsSimpleType(elementType))
				{
					SqlUserColumn col = new SqlUserColumn(elementType, _typeProvider.From(elementType), suq, "", false, _dominatingExpression);
					suq.Columns.Add(col);
					suq.Projection = col;
				}
				else
				{
					// ... otherwise we generate a default projection
					SqlUserRow rowExp = new SqlUserRow(mType.InheritanceRoot, _typeProvider.GetApplicationType((int)ConverterSpecialTypes.Row), suq, _dominatingExpression);
					suq.Projection = _translator.BuildProjection(rowExp, mType, _allowDeferred, null, _dominatingExpression);
				}
			}
			return suq;
		}
		private Type GenerateUserColumn(SqlUserColumn suc)
		{
			// if the user column is not named, it must be the only one!
			if(String.IsNullOrEmpty(suc.Name))
			{
				this.GenerateColumnAccess(suc.ClrType, suc.SqlType, 0, null);
				return suc.ClrType;
			}
			int iName = this.namedColumns.Count;
			this.namedColumns.Add(new NamedColumn(suc.Name, suc.IsRequired));

			Label labNotDefined = gen.DefineLabel();
			Label labExit = gen.DefineLabel();
			LocalBuilder locOrdinal = gen.DeclareLocal(typeof(int));

			// ordinal = session.ordinals[i]
			this.GenerateAccessOrdinals();
			this.GenerateConstInt(iName);
			this.GenerateArrayAccess(typeof(int), false);
			gen.Emit(OpCodes.Stloc, locOrdinal);

			// if (ordinal < 0) goto labNotDefined
			gen.Emit(OpCodes.Ldloc, locOrdinal);
			this.GenerateConstInt(0);
			gen.Emit(OpCodes.Clt);
			gen.Emit(OpCodes.Brtrue, labNotDefined);

			// access column at ordinal position
			this.GenerateColumnAccess(suc.ClrType, suc.SqlType, 0, locOrdinal);
			gen.Emit(OpCodes.Br_S, labExit);

			// not defined?
			gen.MarkLabel(labNotDefined);
			this.GenerateDefault(suc.ClrType, false);

			gen.MarkLabel(labExit);

			return suc.ClrType;
		}
		internal override SqlExpression VisitUserColumn(SqlUserColumn suc)
		{
			_commandStringBuilder.Append(suc.Name);
			return suc;
		}
		internal override SqlExpression VisitUserColumn(SqlUserColumn suc)
		{
			if(this.ingoreExternalRefs && !this.nodeMap.ContainsKey(suc))
			{
				return suc;
			}
			return new SqlUserColumn(suc.ClrType, suc.SqlType, suc.Query, suc.Name, suc.IsRequired, suc.SourceExpression);
		}
		internal override SqlUserQuery VisitUserQuery(SqlUserQuery suq)
		{
			List<SqlExpression> args = new List<SqlExpression>(suq.Arguments.Count);
			foreach(SqlExpression expr in suq.Arguments)
			{
				args.Add(this.VisitExpression(expr));
			}
			SqlExpression projection = this.VisitExpression(suq.Projection);
			SqlUserQuery n = new SqlUserQuery(suq.QueryText, projection, args, suq.SourceExpression);
			this.nodeMap[suq] = n;

			foreach(SqlUserColumn suc in suq.Columns)
			{
				SqlUserColumn dupSuc = new SqlUserColumn(suc.ClrType, suc.SqlType, suc.Query, suc.Name, suc.IsRequired, suc.SourceExpression);
				this.nodeMap[suc] = dupSuc;
				n.Columns.Add(dupSuc);
			}

			return n;
		}
		private SqlNode AccessMember(SqlMember m, SqlExpression expo) {
			SqlExpression exp = expo;

			switch (exp.NodeType) {
				case SqlNodeType.ClientCase: {
					// Distribute into each case.
					SqlClientCase sc = (SqlClientCase)exp;
					Type newClrType = null;
					List<SqlExpression> matches = new List<SqlExpression>();
					List<SqlExpression> values = new List<SqlExpression>();
					foreach (SqlClientWhen when in sc.Whens) {
						SqlExpression newValue = (SqlExpression)AccessMember(m, when.Value);
						if (newClrType == null) {
							newClrType = newValue.ClrType;
						}
						else if (newClrType != newValue.ClrType) {
							throw Error.ExpectedClrTypesToAgree(newClrType, newValue.ClrType);
						}
						matches.Add(when.Match);
						values.Add(newValue);
					}

					SqlExpression result = sql.Case(newClrType, sc.Expression, matches, values, sc.SourceExpression);
					return result;
				}
				case SqlNodeType.SimpleCase: {
					// Distribute into each case.
					SqlSimpleCase sc = (SqlSimpleCase)exp;
					Type newClrType = null;
					List<SqlExpression> newMatches = new List<SqlExpression>();
					List<SqlExpression> newValues = new List<SqlExpression>();
					foreach (SqlWhen when in sc.Whens) {
						SqlExpression newValue = (SqlExpression)AccessMember(m, when.Value);
						if (newClrType == null) {
							newClrType = newValue.ClrType;
						}
						else if (newClrType != newValue.ClrType) {
							throw Error.ExpectedClrTypesToAgree(newClrType, newValue.ClrType);
						}
						newMatches.Add(when.Match);
						newValues.Add(newValue);
					}
					SqlExpression result = sql.Case(newClrType, sc.Expression, newMatches, newValues, sc.SourceExpression);
					return result;
				}
				case SqlNodeType.SearchedCase: {
					// Distribute into each case.
					SqlSearchedCase sc = (SqlSearchedCase)exp;
					List<SqlWhen> whens = new List<SqlWhen>(sc.Whens.Count);
					foreach (SqlWhen when in sc.Whens) {
						SqlExpression value = (SqlExpression)AccessMember(m, when.Value);
						whens.Add(new SqlWhen(when.Match, value));
					}
					SqlExpression @else = (SqlExpression)AccessMember(m, sc.Else);
					return sql.SearchedCase(whens.ToArray(), @else, sc.SourceExpression);
				}
				case SqlNodeType.TypeCase: {
					// We don't allow derived types to map members to different database fields.
					// Therefore, just pick the best SqlNew to call AccessMember on.
					SqlTypeCase tc = (SqlTypeCase)exp;

					// Find the best type binding for this member.
					SqlNew tb = tc.Whens[0].TypeBinding as SqlNew;
					foreach (SqlTypeCaseWhen when in tc.Whens) {
						if (when.TypeBinding.NodeType == SqlNodeType.New) {
							SqlNew sn = (SqlNew)when.TypeBinding;
							if (m.Member.DeclaringType.IsAssignableFrom(sn.ClrType)) {
								tb = sn;
								break;
							}
						}
					}
					return AccessMember(m, tb);
				}
				case SqlNodeType.AliasRef: {
					// convert alias.Member => column
					SqlAliasRef aref = (SqlAliasRef)exp;
					// if its a table, find the matching column
					SqlTable tab = aref.Alias.Node as SqlTable;
					if (tab != null) {
						MetaDataMember mm = GetRequiredInheritanceDataMember(tab.RowType, m.Member);
						Diagnostics.Debug.Assert(mm != null);
						string name = mm.MappedName;
						SqlColumn c = tab.Find(name);
						if (c == null) {
							ProviderType sqlType = sql.Default(mm);
							c = new SqlColumn(m.ClrType, sqlType, name, mm, null, m.SourceExpression);
							c.Alias = aref.Alias;
							tab.Columns.Add(c);
						}
						return new SqlColumnRef(c);
					}
					// if it is a table valued function, find the matching result column                                
					SqlTableValuedFunctionCall fc = aref.Alias.Node as SqlTableValuedFunctionCall;
					if (fc != null) {
						MetaDataMember mm = GetRequiredInheritanceDataMember(fc.RowType, m.Member);
						Diagnostics.Debug.Assert(mm != null);
						string name = mm.MappedName;
						SqlColumn c = fc.Find(name);
						if (c == null) {
							ProviderType sqlType = sql.Default(mm);
							c = new SqlColumn(m.ClrType, sqlType, name, mm, null, m.SourceExpression);
							c.Alias = aref.Alias;
							fc.Columns.Add(c);
						}
						return new SqlColumnRef(c);
					}
					break;
				}
				case SqlNodeType.OptionalValue:
					// convert option(exp).Member => exp.Member
					return this.AccessMember(m, ((SqlOptionalValue)exp).Value);

				case SqlNodeType.OuterJoinedValue: {
					SqlNode n = this.AccessMember(m, ((SqlUnary)exp).Operand);
					SqlExpression e = n as SqlExpression;
					if (e != null) return sql.Unary(SqlNodeType.OuterJoinedValue, e);
					return n;
				}

				case SqlNodeType.Lift:
					return this.AccessMember(m, ((SqlLift)exp).Expression);

				case SqlNodeType.UserRow: {
					// convert UserRow.Member => UserColumn
					SqlUserRow row = (SqlUserRow)exp;
					SqlUserQuery suq = row.Query;
					MetaDataMember mm = GetRequiredInheritanceDataMember(row.RowType, m.Member);
					Diagnostics.Debug.Assert(mm != null);
					string name = mm.MappedName;
					SqlUserColumn c = suq.Find(name);
					if (c == null) {
						ProviderType sqlType = sql.Default(mm);
						c = new SqlUserColumn(m.ClrType, sqlType, suq, name, mm.IsPrimaryKey, m.SourceExpression);
						suq.Columns.Add(c);
					}
					return c;
				}
				case SqlNodeType.New: {
					// convert (new {Member = expr}).Member => expr
					SqlNew sn = (SqlNew)exp;
					SqlExpression e = sn.Find(m.Member);
					if (e != null) {
						return e;
					}
					MetaDataMember mm = sn.MetaType.PersistentDataMembers.FirstOrDefault(p => p.Member == m.Member);
					if (!sn.SqlType.CanBeColumn && mm != null) {
						throw Error.MemberNotPartOfProjection(m.Member.DeclaringType, m.Member.Name);
					}
					break;
				}
				case SqlNodeType.Element:
				case SqlNodeType.ScalarSubSelect: {
					// convert Scalar/Element(select exp).Member => Scalar/Element(select exp.Member) / select exp.Member
					SqlSubSelect sub = (SqlSubSelect)exp;
					SqlAlias alias = new SqlAlias(sub.Select);
					SqlAliasRef aref = new SqlAliasRef(alias);

					SqlSelect saveSelect = this.currentSelect;
					try {
						SqlSelect newSelect = new SqlSelect(aref, alias, sub.SourceExpression);
						this.currentSelect = newSelect;
						SqlNode result = this.Visit(sql.Member(aref, m.Member));

						SqlExpression rexp = result as SqlExpression;
						if (rexp != null) {

							// If the expression is still a Member after being visited, but it cannot be a column, then it cannot be collapsed
							// into the SubSelect because we need to keep track of the fact that this member has to be accessed on the client.
							// This must be done after the expression has been Visited above, because otherwise we don't have
							// enough context to know if the member can be a column or not.
							if (rexp.NodeType == SqlNodeType.Member && !SqlColumnizer.CanBeColumn(rexp)) {
								// If the original member expression is an Element, optimize it by converting to an OuterApply if possible.
								// We have to do this here because we are creating a new member expression based on it, and there are no
								// subsequent visitors that will do this optimization.
								if (this.canUseOuterApply && exp.NodeType == SqlNodeType.Element && this.currentSelect != null) {
									// Reset the currentSelect since we are not going to use the previous SqlSelect that was created
									this.currentSelect = saveSelect;                                            
									this.currentSelect.From = sql.MakeJoin(SqlJoinType.OuterApply, this.currentSelect.From, alias, null, sub.SourceExpression);
									exp = this.VisitExpression(aref);
								}                                        
								return sql.Member(exp, m.Member);
							}

							// Since we are going to make a SubSelect out of this member expression, we need to make
							// sure it gets columnized before it gets to the PostBindDotNetConverter, otherwise only the
							// entire SubSelect will be columnized as a whole. Subsequent columnization does not know how to handle
							// any function calls that may be produced by the PostBindDotNetConverter, but we know how to handle it here.
							newSelect.Selection = rexp;
							newSelect.Selection = this.columnizer.ColumnizeSelection(newSelect.Selection);
							newSelect.Selection = this.ConvertLinks(newSelect.Selection);
							SqlNodeType subType = (rexp is SqlTypeCase || !rexp.SqlType.CanBeColumn) ? SqlNodeType.Element : SqlNodeType.ScalarSubSelect;
							SqlSubSelect subSel = sql.SubSelect(subType, newSelect);
							return this.FoldSubquery(subSel);
						}

						SqlSelect rselect = result as SqlSelect;
						if (rselect != null) {
							SqlAlias ralias = new SqlAlias(rselect);
							SqlAliasRef rref = new SqlAliasRef(ralias);
							newSelect.Selection = this.ConvertLinks(this.VisitExpression(rref));
							newSelect.From = new SqlJoin(SqlJoinType.CrossApply, alias, ralias, null, m.SourceExpression);
							return newSelect;
						}
						throw Error.UnexpectedNode(result.NodeType);
					}
					finally {
						this.currentSelect = saveSelect;
					}
				}
				case SqlNodeType.Value: {
					SqlValue val = (SqlValue)exp;
					if (val.Value == null) {
						return sql.Value(m.ClrType, m.SqlType, null, val.IsClientSpecified, m.SourceExpression);
					}
					else if (m.Member is PropertyInfo) {
						PropertyInfo p = (PropertyInfo)m.Member;
						return sql.Value(m.ClrType, m.SqlType, p.GetValue(val.Value, null), val.IsClientSpecified, m.SourceExpression);
					}
					else {
						FieldInfo f = (FieldInfo)m.Member;
						return sql.Value(m.ClrType, m.SqlType, f.GetValue(val.Value), val.IsClientSpecified, m.SourceExpression);
					}
				}
				case SqlNodeType.Grouping: {
					SqlGrouping g = ((SqlGrouping)exp);
					if (m.Member.Name == "Key") {
						return g.Key;
					}
					break;
				}
				case SqlNodeType.ClientParameter: {
					SqlClientParameter cp = (SqlClientParameter)exp;
					// create new accessor including this member access
					LambdaExpression accessor =
						Expression.Lambda(
										  typeof(Func<,>).MakeGenericType(typeof(object[]), m.ClrType),
							Expression.MakeMemberAccess(cp.Accessor.Body, m.Member),
							cp.Accessor.Parameters
							);
					return new SqlClientParameter(m.ClrType, m.SqlType, accessor, cp.SourceExpression);
				}
				default:
					break;  
			}
			if (m.Expression == exp) {
				return m;
			}
			else {
				return sql.Member(exp, m.Member);
			}
		}