/// <summary> /// Formata e indenta uma SQL. /// A SQL deve ser escrita na conveção do Sequel para um /// resultado otimizado: /// /// @"select * /// from tabela /// where campo = valor" /// .AsSql() /// .Beautify() /// ... /// </summary> /// <param name="sql">A SQL a ser processada.</param> /// <returns>A SQL indentada.</returns> public static Sql Beautify(this Sql sql) { var text = Beautify(sql.ToString()); sql.Text = text; return(sql); }
/// <summary> /// Aplica uma formatação posicional usando String.Format do DotNet. /// Todas as capacidades do String.Format são suportadas. /// Exemplo: /// var texto = "select * from {0} where {1} = {2}"; /// var sql = texto.AsSql(); /// sql.Format("usuario", "nome", "Fulano"); /// </summary> /// <param name="sql">A SQL a ser processada.</param> /// <param name="parameters"> /// A coleção de parâmetros posicionais para substituição. /// </param> /// <returns> /// A mesma instância da SQL obtida como parâmetro para encadeamento /// de operações. /// </returns> public static Sql Format(this Sql sql, params object[] parameters) { var text = sql.ToString(); // Escapando ocorrencias de { e } // - Caracteres { e } sao duplicados // - Em seguida o padrao {{numero}} é retornado para {numero} text = Regex.Replace( text.Replace("{", "{{").Replace("}", "}}"), "[{]([{][\\d]+[}])[}]", "$1" ); sql.Text = string.Format(text, parameters); return(sql); }
/// <summary> /// Aplica o template simplificado de SQL. /// O template suporta a sintaxe: /// /// - @{parametro} /// /// Para realizar substituição no texto. /// /// Por exemplo, um nome de tabela pode ser definido dinamicamente e declarado /// no template como: /// /// SELECT * FROM @{nome_tabela} WHERE id = @id_tabela /// /// </summary> /// <param name="sql">The SQL.</param> public static void ApplyStandardTemplate(Sql sql) { var text = sql.ToString(); var names = from name in sql.ParameterNames orderby name.Length descending, name select name; foreach (var name in names) { var value = Commander.CreateSqlCompatibleValue(sql[name]); var key = "@{" + name + "}"; var keyValue = value.ToString(); text = text.Replace(key, keyValue); } sql.Text = text; }
/// <summary> /// Aplica uma substituição de parâmetro estendido no texto indicado. /// /// A função é complemento da função ApplyExtendedTemplate com a responsabilidade /// de localizar no texto as ocorrências do padrão "campo = {parametro}" realizando /// a substituição pela instrução comparadora definitiva. /// /// A porção "{parametro}" pode conter um valor booliano na forma: {parametro|valor}. /// Por padrão, quando o valor de um parâmetro não é definido o parâmetro é considerado /// bem sucedido pra qualquer registro, tornando nulo o parâmetro para fins de filtro. /// Este comportamento pode ser invertido pelo uso de "{parametro|0}" ou "{parametro|false}". /// Neste caso, o parâmetro é considerado mal sucedido e todos os registros passam a /// ser rejeitados nesta condição. /// </summary> /// <param name="text">O texto que sofrerá a substituição.</param> /// <param name="parameter">Nome do parametro procurado.</param> /// <param name="replacement">Texto que substituirá o padrão "campo = {parametro}"</param> /// <returns>O texto com a substituição efetuada.</returns> private static string ReplaceTemplate(Sql sql, string parameter, string replacement) { Regex regex; MatchCollection matches; var text = sql.ToString(); // Aplicando a substituição da instrução: // @param is [not] set // Que deve ser tornar: // 1=1 quando verdadeiro // 1=0 quando falso regex = new Regex("(@" + parameter + @"\s+is(\s+not)?\s+set)"); matches = regex.Matches(text); foreach (var match in matches.Cast <Match>()) { var isSet = replacement != null; var negate = match.Groups[2].Value.Contains("not"); if (negate) { isSet = !isSet; } var criteria = isSet ? "1=1" : "1=0"; text = text.Replace(match.Value, criteria); } // Aplicando a substituição da instrução: // target [not] matches [if set] @param // Que deve ser substituído por "replacement", produzindo por exemplo: // target = valor // target >= valor // target <= valor // target in (valor, ...) // target like valor // target between valor and valor // Sendo que: // quando "not" está presente a condição é invertida. // quando "if set" está presente, se a condicao for nula, o resultado é verdadeiro: // 1=1 // quando "if set" não está presente, se a condicao for nula, o resultado é falso: // 1=0 regex = new Regex(@"(?:([a-zA-Z_.]+)|([a-zA-Z0-9_]*[(](?>[(](?<c>)|[^()]+|[)](?<-c>))*(?(c)(?!))[)]))\s+(not\s+)?matches\s+(if\s+set\s+)?@" + parameter); matches = regex.Matches(text); foreach (var match in matches.Cast <Match>()) { string newSentence = null; if (replacement == null) { // quando "if set" está presente, se a condicao for nula, o resultado é verdadeiro: // 1=1 // quando "if set" não está presente, se a condicao for nula, o resultado é falso: // 1=0 var ifSet = match.Groups[4].Value.Contains("set"); newSentence = ifSet ? "1=1" : "1=0"; } else { var leftSide = match.Groups[1].Value + match.Groups[2].Value; newSentence = string.Format(replacement, leftSide); var negate = match.Groups[3].Value.Contains("not"); if (negate) { newSentence = "not " + newSentence; } } text = text.Replace(match.Value, newSentence); } return(text); }