<commit_before>﻿using Silk.Data.Modelling;
using Silk.Data.SQL.Expressions;
using Silk.Data.SQL.ORM.Modelling;
using Silk.Data.SQL.ORM.Operations.Expressions;
using Silk.Data.SQL.Queries;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Silk.Data.SQL.ORM.Operations
{
	public class SelectOperation<T> : DataOperationWithResult<ICollection<T>>
	{
		private readonly static string[] _selfPath = new string[] { "." };
		private static readonly Dictionary<Type, Func<QueryResult, int, object>> _typeReaders =
			new Dictionary<Type, Func<QueryResult, int, object>>()
			{
				{ typeof(bool), (q,o) => q.GetBoolean(o) },
				{ typeof(byte), (q,o) => q.GetByte(o) },
				{ typeof(short), (q,o) => q.GetInt16(o) },
				{ typeof(int), (q,o) => q.GetInt32(o) },
				{ typeof(long), (q,o) => q.GetInt64(o) },
				{ typeof(float), (q,o) => q.GetFloat(o) },
				{ typeof(double), (q,o) => q.GetDouble(o) },
				{ typeof(decimal), (q,o) => q.GetDecimal(o) },
				{ typeof(string), (q,o) => q.GetString(o) },
				{ typeof(Guid), (q,o) => q.GetGuid(o) },
				{ typeof(DateTime), (q,o) => q.GetDateTime(o) },
				{ typeof(bool?), (q,o) => q.GetBoolean(o) },
				{ typeof(byte?), (q,o) => q.GetByte(o) },
				{ typeof(short?), (q,o) => q.GetInt16(o) },
				{ typeof(int?), (q,o) => q.GetInt32(o) },
				{ typeof(long?), (q,o) => q.GetInt64(o) },
				{ typeof(float?), (q,o) => q.GetFloat(o) },
				{ typeof(double?), (q,o) => q.GetDouble(o) },
				{ typeof(decimal?), (q,o) => q.GetDecimal(o) },
				{ typeof(Guid?), (q,o) => q.GetGuid(o) },
				{ typeof(DateTime?), (q,o) => q.GetDateTime(o) },
			};

		private readonly QueryExpression _query;
		private readonly ProjectionModel _projectionModel;
		private readonly TypeModel<T> _typeModel;
		private ICollection<T> _result;

		public override ICollection<T> Result => _result;
		public override bool CanBeBatched => true;

		public SelectOperation(QueryExpression query, ProjectionModel projectionModel)
		{
			_query = query;
			_projectionModel = projectionModel;
			_typeModel = TypeModel.GetModelOf<T>();
		}

		public override QueryExpression GetQuery()
		{
			return _query;
		}

		public override void ProcessResult(QueryResult queryResult)
		{
			if (_projectionModel == null)
			{
				if (!_typeReaders.TryGetValue(typeof(T), out var readerFunc))
					throw new InvalidOperationException("Cannot read data type.");
				var result = new List<T>();
				while (queryResult.Read())
				{
					if (queryResult.IsDBNull(0))
						result.Add(default(T));
					else
						result.Add((T)readerFunc(queryResult, 0));
				}
				queryResult.NextResult();
				_result = result;
			}
			else
			{
				var result = new List<IModelReadWriter>();
				var reader = new QueryResultReader(_projectionModel, queryResult);

				while (queryResult.Read())
				{
					var writer = new ObjectReadWriter(null, _typeModel, typeof(T));
					_projectionModel.Mapping.PerformMapping(reader, writer);
					result.Add(writer);
				}
				queryResult.NextResult();

				foreach (var manyRelatedObjectField in _projectionModel.Fields.OfType<IManyRelatedObjectField>())
				{
					if (!queryResult.HasRows)
						continue;

					reader = new QueryResultReader(manyRelatedObjectField.RelatedObjectModel, queryResult);
					var mapper = manyRelatedObjectField.CreateObjectMapper($"__IDENT__{manyRelatedObjectField.FieldName}");
					mapper.PerformMapping(queryResult, reader, result);
					queryResult.NextResult();
				}

				_result = result.Select(q => q.ReadField<T>(_selfPath, 0)).ToArray();
			}
		}

		public override async Task ProcessResultAsync(QueryResult queryResult)
		{
			if (_projectionModel == null)
			{
				if (!_typeReaders.TryGetValue(typeof(T), out var readerFunc))
					throw new InvalidOperationException("Cannot read data type.");
				var result = new List<T>();
				while (await queryResult.ReadAsync())
				{
					if (queryResult.IsDBNull(0))
						result.Add(default(T));
					else
						result.Add((T)readerFunc(queryResult, 0));
				}
				await queryResult.NextResultAsync();
				_result = result;
			}
			else
			{
				var result = new List<IModelReadWriter>();
				var reader = new QueryResultReader(_projectionModel, queryResult);

				while (await queryResult.ReadAsync())
				{
					var writer = new ObjectReadWriter(null, _typeModel, typeof(T));
					_projectionModel.Mapping.PerformMapping(reader, writer);
					result.Add(writer);
				}
				await queryResult.NextResultAsync();

				foreach (var manyRelatedObjectField in _projectionModel.Fields.OfType<IManyRelatedObjectField>())
				{
					if (!queryResult.HasRows)
						continue;

					reader = new QueryResultReader(manyRelatedObjectField.RelatedObjectModel, queryResult);
					var mapper = manyRelatedObjectField.CreateObjectMapper($"__IDENT__{manyRelatedObjectField.FieldName}");
					await mapper.PerformMappingAsync(queryResult, reader, result);
					await queryResult.NextResultAsync();
				}

				_result = result.Select(q => q.ReadField<T>(_selfPath, 0)).ToArray();
			}
		}
	}

	public class SelectOperation
	{
		public static SelectOperation<TEntity> Create<TEntity>(EntityModel<TEntity> model,
			Condition where = null, Condition having = null,
			OrderBy[] orderBy = null, GroupBy[] groupBy = null,
			int? offset = null, int? limit = null)
		{
			return Create<TEntity>(model, model, where, having, orderBy, groupBy, offset, limit);
		}

		public static SelectOperation<TProjection> Create<TEntity, TProjection>(EntityModel<TEntity> model,
			Condition where = null, Condition having = null,
			OrderBy[] orderBy = null, GroupBy[] groupBy = null,
			int? offset = null, int? limit = null)
		{
			return Create<TProjection>(model.GetProjection<TProjection>(), model, where, having, orderBy, groupBy, offset, limit);
		}

		public static SelectOperation<int> CreateCount<TEntity>(EntityModel<TEntity> model,
			Condition where = null, Condition having = null, GroupBy[] groupBy = null)
		{
			var query = CreateQuery(new[] { QueryExpression.CountFunction() }, model,
				where, having, null, groupBy);
			return new SelectOperation<int>(query, null);
		}

		private static SelectOperation<TProjection> Create<TProjection>(ProjectionModel projectionModel, EntityModel entityModel,
			Condition where = null, Condition having = null,
			OrderBy[] orderBy = null, GroupBy[] groupBy = null,<commit_msg>Change COUNT query to specify COUNTing QueryExpression.All().<commit_after>﻿using Silk.Data.Modelling;
using Silk.Data.SQL.Expressions;
using Silk.Data.SQL.ORM.Modelling;
using Silk.Data.SQL.ORM.Operations.Expressions;
using Silk.Data.SQL.Queries;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Silk.Data.SQL.ORM.Operations
{
	public class SelectOperation<T> : DataOperationWithResult<ICollection<T>>
	{
		private readonly static string[] _selfPath = new string[] { "." };
		private static readonly Dictionary<Type, Func<QueryResult, int, object>> _typeReaders =
			new Dictionary<Type, Func<QueryResult, int, object>>()
			{
				{ typeof(bool), (q,o) => q.GetBoolean(o) },
				{ typeof(byte), (q,o) => q.GetByte(o) },
				{ typeof(short), (q,o) => q.GetInt16(o) },
				{ typeof(int), (q,o) => q.GetInt32(o) },
				{ typeof(long), (q,o) => q.GetInt64(o) },
				{ typeof(float), (q,o) => q.GetFloat(o) },
				{ typeof(double), (q,o) => q.GetDouble(o) },
				{ typeof(decimal), (q,o) => q.GetDecimal(o) },
				{ typeof(string), (q,o) => q.GetString(o) },
				{ typeof(Guid), (q,o) => q.GetGuid(o) },
				{ typeof(DateTime), (q,o) => q.GetDateTime(o) },
				{ typeof(bool?), (q,o) => q.GetBoolean(o) },
				{ typeof(byte?), (q,o) => q.GetByte(o) },
				{ typeof(short?), (q,o) => q.GetInt16(o) },
				{ typeof(int?), (q,o) => q.GetInt32(o) },
				{ typeof(long?), (q,o) => q.GetInt64(o) },
				{ typeof(float?), (q,o) => q.GetFloat(o) },
				{ typeof(double?), (q,o) => q.GetDouble(o) },
				{ typeof(decimal?), (q,o) => q.GetDecimal(o) },
				{ typeof(Guid?), (q,o) => q.GetGuid(o) },
				{ typeof(DateTime?), (q,o) => q.GetDateTime(o) },
			};

		private readonly QueryExpression _query;
		private readonly ProjectionModel _projectionModel;
		private readonly TypeModel<T> _typeModel;
		private ICollection<T> _result;

		public override ICollection<T> Result => _result;
		public override bool CanBeBatched => true;

		public SelectOperation(QueryExpression query, ProjectionModel projectionModel)
		{
			_query = query;
			_projectionModel = projectionModel;
			_typeModel = TypeModel.GetModelOf<T>();
		}

		public override QueryExpression GetQuery()
		{
			return _query;
		}

		public override void ProcessResult(QueryResult queryResult)
		{
			if (_projectionModel == null)
			{
				if (!_typeReaders.TryGetValue(typeof(T), out var readerFunc))
					throw new InvalidOperationException("Cannot read data type.");
				var result = new List<T>();
				while (queryResult.Read())
				{
					if (queryResult.IsDBNull(0))
						result.Add(default(T));
					else
						result.Add((T)readerFunc(queryResult, 0));
				}
				queryResult.NextResult();
				_result = result;
			}
			else
			{
				var result = new List<IModelReadWriter>();
				var reader = new QueryResultReader(_projectionModel, queryResult);

				while (queryResult.Read())
				{
					var writer = new ObjectReadWriter(null, _typeModel, typeof(T));
					_projectionModel.Mapping.PerformMapping(reader, writer);
					result.Add(writer);
				}
				queryResult.NextResult();

				foreach (var manyRelatedObjectField in _projectionModel.Fields.OfType<IManyRelatedObjectField>())
				{
					if (!queryResult.HasRows)
						continue;

					reader = new QueryResultReader(manyRelatedObjectField.RelatedObjectModel, queryResult);
					var mapper = manyRelatedObjectField.CreateObjectMapper($"__IDENT__{manyRelatedObjectField.FieldName}");
					mapper.PerformMapping(queryResult, reader, result);
					queryResult.NextResult();
				}

				_result = result.Select(q => q.ReadField<T>(_selfPath, 0)).ToArray();
			}
		}

		public override async Task ProcessResultAsync(QueryResult queryResult)
		{
			if (_projectionModel == null)
			{
				if (!_typeReaders.TryGetValue(typeof(T), out var readerFunc))
					throw new InvalidOperationException("Cannot read data type.");
				var result = new List<T>();
				while (await queryResult.ReadAsync())
				{
					if (queryResult.IsDBNull(0))
						result.Add(default(T));
					else
						result.Add((T)readerFunc(queryResult, 0));
				}
				await queryResult.NextResultAsync();
				_result = result;
			}
			else
			{
				var result = new List<IModelReadWriter>();
				var reader = new QueryResultReader(_projectionModel, queryResult);

				while (await queryResult.ReadAsync())
				{
					var writer = new ObjectReadWriter(null, _typeModel, typeof(T));
					_projectionModel.Mapping.PerformMapping(reader, writer);
					result.Add(writer);
				}
				await queryResult.NextResultAsync();

				foreach (var manyRelatedObjectField in _projectionModel.Fields.OfType<IManyRelatedObjectField>())
				{
					if (!queryResult.HasRows)
						continue;

					reader = new QueryResultReader(manyRelatedObjectField.RelatedObjectModel, queryResult);
					var mapper = manyRelatedObjectField.CreateObjectMapper($"__IDENT__{manyRelatedObjectField.FieldName}");
					await mapper.PerformMappingAsync(queryResult, reader, result);
					await queryResult.NextResultAsync();
				}

				_result = result.Select(q => q.ReadField<T>(_selfPath, 0)).ToArray();
			}
		}
	}

	public class SelectOperation
	{
		public static SelectOperation<TEntity> Create<TEntity>(EntityModel<TEntity> model,
			Condition where = null, Condition having = null,
			OrderBy[] orderBy = null, GroupBy[] groupBy = null,
			int? offset = null, int? limit = null)
		{
			return Create<TEntity>(model, model, where, having, orderBy, groupBy, offset, limit);
		}

		public static SelectOperation<TProjection> Create<TEntity, TProjection>(EntityModel<TEntity> model,
			Condition where = null, Condition having = null,
			OrderBy[] orderBy = null, GroupBy[] groupBy = null,
			int? offset = null, int? limit = null)
		{
			return Create<TProjection>(model.GetProjection<TProjection>(), model, where, having, orderBy, groupBy, offset, limit);
		}

		public static SelectOperation<int> CreateCount<TEntity>(EntityModel<TEntity> model,
			Condition where = null, Condition having = null, GroupBy[] groupBy = null)
		{
			var query = CreateQuery(new[] { QueryExpression.CountFunction(QueryExpression.All()) }, model,
				where, having, null, groupBy);
			return new SelectOperation<int>(query, null);
		}

		private static SelectOperation<TProjection> Create<TProjection>(ProjectionModel projectionModel, EntityModel entityModel,
			Condition where = null, Condition having = null,
			OrderBy[] orderBy = null, GroupBy[] groupBy = null,
{'fraction_non_alphanumeric': 0.14033580632565404, 'fraction_numerical': 0.0028114017961733697, 'mean_word_length': 4.184615384615385, 'pattern_counts': {'":': 0, '<': 71, '<?xml version=': 0, '>': 121, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3099120', 'n_tokens_mistral': 4895, 'n_tokens_neox': 4074, 'n_words': 861}