// // Copyright (c) Microsoft. All Rights Reserved. // using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Linq; using Microsoft.Kusto.ServiceLayer.DataSource.Metadata; namespace Microsoft.Kusto.ServiceLayer.DataSource { public static class KustoQueryUtils { public const string StatementSeparator = "\n | "; // Start each statement on a new line. Not required by Kusto, but doing this for readability of scripts generated from here. /// /// Escape table/column/database names for a Kusto query. /// /// The name to be escaped /// Always escape if this flag is set /// The escaped string public static string EscapeName(string name, bool alwaysEscape = false) { if (name.StartsWith("[@") || name == "*") // Field already escaped. No escaping required for '*' operand { return name; } string result = name; Regex rx = new Regex("[^_a-zA-Z0-9]"); string [] kustoKeywordList = {"and", "anomalychart", "areachart", "asc", "barchart", "between", "bool", "boolean", "by", "columnchart", "consume", "contains", "containscs", "count", "date", "datetime", "default", "desc", "distinct", "double", "dynamic", "endswith", "evaluate", "extend", "false", "filter", "find", "first", "flags", "float", "getschema", "has", "hasprefix", "hassuffix", "in", "int", "join", "journal", "kind", "ladderchart", "last", "like", "limit", "linechart", "long", "materialize", "mvexpand", "notcontains", "notlike", "of", "or", "order", "parse", "piechart", "pivotchart", "print", "project", "queries", "real", "regex", "sample", "scatterchart", "search", "set", "sort", "stacked", "stacked100", "stackedareachart", "startswith", "string", "summarize", "take", "time", "timechart", "timeline", "timepivot", "timespan", "to", "top", "toscalar", "true", "union", "unstacked", "viewers", "where", "withsource"}; // add more keywords here var escapeName = rx.IsMatch(name) || kustoKeywordList.Any(name.Contains) || alwaysEscape; if (escapeName) { if (name.IndexOf('"') > -1) { result = "[@'" + name + "']"; } else { result = "[@\"" + name + "\"]"; } } return result; } public static bool IsClusterLevelQuery(string query) { string [] clusterLevelQueryPrefixes = { ".show databases", ".show schema" }; return clusterLevelQueryPrefixes.Any(query.StartsWith); } /// /// Adds an object of type DataSourceObjectMetadata to a dictionary>. If the key exists then the item is added /// to the list. If not then the key is created and then added. /// /// The dictionary of the dictionary that the list should be added to. /// The key to be added. /// The metadata to be added to the list. /// public static void SafeAdd(this Dictionary> dictionary, string key, T metadata) where T : DataSourceObjectMetadata { if (dictionary.ContainsKey(key)) { if (dictionary[key].ContainsKey(metadata.Name)) { return; } dictionary[key].Add(metadata.Name, metadata); } else { dictionary[key] = new Dictionary {{metadata.Name, metadata}}; } } /// /// Add a range to a dictionary of ConcurrentDictionary. Adds range to existing IEnumerable within dictionary /// at the same key. /// /// /// /// /// public static void AddRange(this ConcurrentDictionary> dictionary, string key, List metadatas) where T : DataSourceObjectMetadata { if (dictionary.ContainsKey(key)) { metadatas.AddRange(dictionary[key]); } dictionary[key] = metadatas.OrderBy(x => x.PrettyName, StringComparer.OrdinalIgnoreCase).ToList(); } } }