/// <summary> /// Realiza a aplicação do template. /// /// Por padrão, todos os parâmetros atribuídos são estocados na instância /// de Sql e aplicados à string SQL apenas no ato da execução da SQL. /// /// Este método aplica o template imediatamente, modificando a SQL original, /// a menos que desativado pela configuração SequelSettings.QueryTemplateEnabled. /// /// Os parâmetros são aplicados ao template de duas formas: /// /// 1. Posicional /// Com ouso de String.Format parâmetros podem ser adicionados à string /// na forma {0}, {1}, etc. /// /// 2. Nomeado /// Parâmetros nomeados podem aparecer na string na forma @{nome}. /// /// Exemplo: /// var texto = "select * from {0} where @{campo} = @valor"; /// var sql = texto.AsSql(); /// sql.Format("usuario"); /// sql.Set("campo", "login"); /// sql.Set("valor", "Fulano"); /// sql.ApplyTemplate(); /// /// Resultado: /// select * from usuario where login = @valor /// /// </summary> /// <param name="sql">A SQL a ser processada.</param> /// <returns> /// A mesma instância da SQL obtida como parâmetro para encadeamento /// de operações. /// </returns> public static Sql ApplyTemplate(this Sql sql) { return(ApplyTemplate(sql, false)); }
/// <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); }
/// <summary> /// Constrói a condição que substituirá o template baseado na instrução: /// - target [always] matches @parametro /// /// Se o valor do parâmetro não tiver sido definido o critério retornado será nulo. /// /// O critério criado contém o termo "{0}" que deve ser substituído pela /// parte "target" à direta da instrução matches. /// /// Por exemplo, em: /// Campo matches @nome /// /// O critério produzir pode ser algo como: /// {0} like @nome /// /// Que deve então ser substituído com String.Format() pela parte à direta /// da instrução, neste caso "Campo": /// String.Format("{0} like @nome", "Campo"); /// /// Produzindo como resultado: /// Campo like @nome /// </summary> /// <param name="parameter">Nome do parâmetro que será substituído.</param> /// <param name="value">Valor atribuído ao parâmetro.</param> /// <param name="newParameters"> /// Coleção dos parâmetros conhecidos. /// Novos parâmetros podem ser criados nesta coleção para suporte ao critério criado. /// </param> /// <returns>A condição que substituirá o template.</returns> private static string CreateCriteria(Sql sql, string parameter, object value) { if (value == null || (value as Values)?.IsNull == true) { return(null); } if ((value as Values)?.IsValue == true) { value = ((Values)value).Value; } string criteria = null; if (value is Sql) { var innerSql = (Sql)value; innerSql.ApplyTemplate(false); var innerText = innerSql.Text + "\n"; // cada parametro recebera um sufixo para diferenciacao de parametros // que já existem na instância de Sql. foreach (var parameterName in innerSql.ParameterNames) { var localName = sql.KeyGen.Rename(parameterName); sql[localName] = innerSql[parameterName]; string pattern; string replacement; pattern = "@" + parameterName + "([^0-9A-Za-z_])"; replacement = "@" + localName + "$1"; innerText = Regex.Replace(innerText, pattern, replacement); pattern = "@{" + parameterName + "}"; replacement = "@{" + localName + "}"; innerText = innerText.Replace(pattern, replacement); } criteria = "{0} in (" + innerText + ")"; } else if ((value as Values)?.IsArray == true) { var items = Commander.CreateSqlCompatibleValue(value); var values = string.Join(",", items); criteria = "{0} in (" + values + ")"; } else if ((value as Values)?.IsRange == true) { var range = (Values)value; if (range.Min != null && range.Max != null) { var min = sql.KeyGen.Rename(parameter); var max = sql.KeyGen.Rename(parameter); criteria = "{0} between @" + min + " and @" + max; sql[min] = range.Min; sql[max] = range.Max; } else if (range.Min != null) { var name = sql.KeyGen.Rename(parameter); criteria = "{0} >= @" + name; sql[name] = range.Min; } else if (range.Max != null) { var name = sql.KeyGen.Rename(parameter); criteria = "{0} <= @" + name; sql[name] = range.Max; } else { criteria = "{0} = @" + parameter; sql[parameter] = value; } } else if ((value as Values)?.IsText == true) { var text = (Values)value; if (text.HasWildcard) { criteria = "{0} like @" + parameter; sql[parameter] = text.Text; } else { criteria = "{0} = @" + parameter; sql[parameter] = text.Text; } } else { criteria = "{0} = @" + parameter; sql[parameter] = value; } return(criteria); }