Пример #1
0
        /// <summary>
        /// Executes a multi-record insert query clause with <em>SELECT UNION ALL</em>.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="storage">The storage to use.</param>
        /// <param name="tableName">The table name to against which to execute the query.</param>
        /// <param name="parameters">The parameters to insert.</param>
        /// <param name="nameMap">If provided, maps property names from <typeparamref name="T"/> to ones provided in the map.</param>
        /// <param name="onlyOnceColumns">If given, SQL parameter values for the given <typeparamref name="T"/> property types are generated only once. Effective only when <paramref name="useSqlParams"/> is <em>TRUE</em>.</param>
        /// <param name="useSqlParams"><em>TRUE</em> if the query should be in parameterized form. <em>FALSE</em> otherwise.</param>
        /// <returns>The rows affected.</returns>
        public static Task <int> ExecuteMultipleInsertIntoAsync <T>(this IRelationalStorage storage, string tableName, IEnumerable <T> parameters, IReadOnlyDictionary <string, string> nameMap = null, IEnumerable <string> onlyOnceColumns = null, bool useSqlParams = true)
        {
            if (string.IsNullOrWhiteSpace(tableName))
            {
                throw new ArgumentException("The name must be a legal SQL table name", "tableName");
            }

            if (parameters == null)
            {
                throw new ArgumentNullException("parameters");
            }

            var storageConsts = DbConstantsStore.GetDbConstants(storage.InvariantName);

            var startEscapeIndicator = storageConsts.StartEscapeIndicator;
            var endEscapeIndicator   = storageConsts.EndEscapeIndicator;

            //SqlParameters map is needed in case the query needs to be parameterized in order to avoid two
            //reflection passes as first a query needs to be constructed and after that when a database
            //command object has been created, parameters need to be provided to them.
            var          sqlParameters            = new Dictionary <string, object>();
            const string insertIntoValuesTemplate = "INSERT INTO {0} ({1}) SELECT {2};";
            var          columns = string.Empty;
            var          values  = new List <string>();

            if (parameters.Any())
            {
                //Type and property information are the same for all of the objects.
                //The following assumes the property names will be retrieved in the same
                //order as is the index iteration done.
                var onlyOnceRow = new List <string>();
                var properties  = parameters.First().GetType().GetProperties();
                columns = string.Join(",", nameMap == null ? properties.Select(pn => string.Format("{0}{1}{2}", startEscapeIndicator, pn.Name, endEscapeIndicator)) : properties.Select(pn => string.Format("{0}{1}{2}", startEscapeIndicator, (nameMap.ContainsKey(pn.Name) ? nameMap[pn.Name] : pn.Name), endEscapeIndicator)));
                if (onlyOnceColumns != null && onlyOnceColumns.Any())
                {
                    var onlyOnceProperties = properties.Where(pn => onlyOnceColumns.Contains(pn.Name)).Select(pn => pn).ToArray();
                    var onlyOnceData       = parameters.First();
                    for (int i = 0; i < onlyOnceProperties.Length; ++i)
                    {
                        var currentProperty = onlyOnceProperties[i];
                        var parameterValue  = currentProperty.GetValue(onlyOnceData, null);
                        if (useSqlParams)
                        {
                            var parameterName = string.Format("@{0}", (nameMap.ContainsKey(onlyOnceProperties[i].Name) ? nameMap[onlyOnceProperties[i].Name] : onlyOnceProperties[i].Name));
                            onlyOnceRow.Add(parameterName);
                            sqlParameters.Add(parameterName, parameterValue);
                        }
                        else
                        {
                            onlyOnceRow.Add(string.Format(sqlFormatProvider, "{0}", parameterValue));
                        }
                    }
                }

                var dataRows        = new List <string>();
                var multiProperties = onlyOnceColumns == null ? properties : properties.Where(pn => !onlyOnceColumns.Contains(pn.Name)).Select(pn => pn).ToArray();
                int parameterCount  = 0;
                foreach (var row in parameters)
                {
                    for (int i = 0; i < multiProperties.Length; ++i)
                    {
                        var currentProperty = multiProperties[i];
                        var parameterValue  = currentProperty.GetValue(row, null);
                        if (useSqlParams)
                        {
                            var parameterName = string.Format(indexedParameterTemplate, parameterCount);
                            dataRows.Add(parameterName);
                            sqlParameters.Add(parameterName, parameterValue);
                            ++parameterCount;
                        }
                        else
                        {
                            dataRows.Add(string.Format(sqlFormatProvider, "{0}", parameterValue));
                        }
                    }

                    values.Add(string.Format("{0}", string.Join(",", onlyOnceRow.Concat(dataRows))));
                    dataRows.Clear();
                }
            }

            var query = string.Format(insertIntoValuesTemplate, tableName, columns, string.Join(storageConsts.UnionAllSelectTemplate, values));

            return(storage.ExecuteAsync(query, command =>
            {
                if (useSqlParams)
                {
                    foreach (var sp in sqlParameters)
                    {
                        var p = command.CreateParameter();
                        p.ParameterName = sp.Key;
                        p.Value = sp.Value ?? DBNull.Value;
                        p.Direction = ParameterDirection.Input;
                        command.Parameters.Add(p);
                    }
                }
            }));
        }
Пример #2
0
        /// <summary>
        /// Inserts the given statistics counters to the Orleans database.
        /// </summary>
        /// <param name="deploymentId">The deployment ID.</param>
        /// <param name="hostName">The hostname.</param>
        /// <param name="siloOrClientName">The silo or client name.</param>
        /// <param name="id">The silo address or client ID.</param>
        /// <param name="counters">The counters to be inserted.</param>
        internal Task InsertStatisticsCountersAsync(string deploymentId, string hostName, string siloOrClientName,
                                                    string id, List <ICounter> counters)
        {
            var queryTemplate = dbStoredQueries.InsertOrleansStatisticsKey;

            //Zero statistic values mean either that the system is not running or no updates. Such values are not inserted and pruned
            //here so that no insert query or parameters are generated.
            counters =
                counters.Where(i => !"0".Equals(i.IsValueDelta ? i.GetDeltaString() : i.GetValueString())).ToList();
            if (counters.Count == 0)
            {
                return(TaskDone.Done);
            }

            //Note that the following is almost the same as RelationalStorageExtensions.ExecuteMultipleInsertIntoAsync
            //the only difference being that some columns are skipped. Likely it would be beneficial to introduce
            //a "skip list" to RelationalStorageExtensions.ExecuteMultipleInsertIntoAsync.

            //The template contains an insert for online. The part after SELECT is copied
            //out so that certain parameters can be multiplied by their count. Effectively
            //this turns a query of type (transaction details vary by vendor)
            //BEGIN TRANSACTION; INSERT INTO [OrleansStatisticsTable] <columns> SELECT <variables>; COMMIT TRANSACTION;
            //to BEGIN TRANSACTION; INSERT INTO [OrleansStatisticsTable] <columns> SELECT <variables>; UNION ALL <variables> COMMIT TRANSACTION;
            //where the UNION ALL is multiplied as many times as there are counters to insert.
            int startFrom = queryTemplate.IndexOf("SELECT", StringComparison.Ordinal) + "SELECT".Length + 1;
            //This +1 is to have a space between SELECT and the first parameter name to not to have a SQL syntax error.
            int lastSemicolon  = queryTemplate.LastIndexOf(";", StringComparison.Ordinal);
            int endTo          = lastSemicolon > 0 ? queryTemplate.LastIndexOf(";", lastSemicolon - 1, StringComparison.Ordinal) : -1;
            var template       = queryTemplate.Substring(startFrom, endTo - startFrom);
            var parameterNames = template.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(i => i.Trim()).ToArray();
            var collectionOfParametersToBeUnionized = new List <string>();
            var parametersToBeUnioned = new string[parameterNames.Length];

            for (int counterIndex = 0; counterIndex < counters.Count; ++counterIndex)
            {
                for (int parameterNameIndex = 0; parameterNameIndex < parameterNames.Length; ++parameterNameIndex)
                {
                    if (InsertStatisticsMultiupdateColumns.Contains(parameterNames[parameterNameIndex]))
                    {
                        //These parameters change for each row. The format is
                        //@StatValue0, @StatValue1, @StatValue2, ... @sStatValue{counters.Count}.
                        parametersToBeUnioned[parameterNameIndex] = $"{parameterNames[parameterNameIndex]}{counterIndex}";
                    }
                    else
                    {
                        //These parameters remain constant for every and each row.
                        parametersToBeUnioned[parameterNameIndex] = parameterNames[parameterNameIndex];
                    }
                }
                collectionOfParametersToBeUnionized.Add($"{string.Join(",", parametersToBeUnioned)}");
            }

            var storageConsts = DbConstantsStore.GetDbConstants(storage.InvariantName);
            var query         = queryTemplate.Replace(template, string.Join(storageConsts.UnionAllSelectTemplate, collectionOfParametersToBeUnionized));

            return(ExecuteAsync(query, command =>
                                new DbStoredQueries.Columns(command)
            {
                DeploymentId = deploymentId,
                HostName = hostName,
                Counters = counters,
                Name = siloOrClientName,
                Id = id
            }));
        }