5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
\r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
\r
8 // of this software and associated documentation files (the "Software"), to deal
\r
9 // in the Software without restriction, including without limitation the rights
\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
11 // copies of the Software, and to permit persons to whom the Software is
\r
12 // furnished to do so, subject to the following conditions:
\r
14 // The above copyright notice and this permission notice shall be included in
\r
15 // all copies or substantial portions of the Software.
\r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
\r
28 using System.Collections;
\r
29 using System.Collections.Generic;
\r
32 namespace DbLinq.Data.Linq.Sql
\r
35 /// An SqlStatement is a literal SQL request, composed of different parts (SqlPart)
\r
36 /// each part being either a parameter or a literal string
\r
41 class SqlStatementBuilder
\r
43 public readonly List<SqlPart> Parts = new List<SqlPart>();
\r
46 /// Returns part at given index
\r
48 /// <param name="index"></param>
\r
49 /// <returns></returns>
\r
50 public SqlPart this[int index]
\r
52 get { return Parts[index]; }
\r
56 /// Creates a new SqlStatement based on the current and appending new SqlParts
\r
58 /// <param name="newParts"></param>
\r
59 /// <returns></returns>
\r
60 public void Append(IList<SqlPart> newParts)
\r
62 foreach (var part in newParts)
\r
63 AddPart(Parts, part);
\r
67 /// Appends a single part, including (useless) optimizations
\r
69 /// <param name="parts"></param>
\r
70 /// <param name="index"></param>
\r
71 /// <param name="part"></param>
\r
72 public static void InsertPart(IList<SqlPart> parts, int index, SqlPart part)
\r
74 // optimization if top part is a literal, and the one we're adding is a literal too
\r
75 // in this case, we combine both
\r
76 // (this is useless, just pretty)
\r
77 if (part is SqlLiteralPart && index > 0 && parts[index - 1] is SqlLiteralPart)
\r
79 parts[index - 1] = new SqlLiteralPart(parts[index - 1].Sql + part.Sql);
\r
82 parts.Insert(index, part);
\r
86 /// Adds the part to the given parts list.
\r
88 /// <param name="parts">The parts.</param>
\r
89 /// <param name="part">The part.</param>
\r
90 public static void AddPart(IList<SqlPart> parts, SqlPart part)
\r
92 InsertPart(parts, parts.Count, part);
\r
96 /// Joins statements, separated by a given statement
\r
98 /// <param name="sqlStatement"></param>
\r
99 /// <param name="sqlStatements"></param>
\r
100 /// <returns></returns>
\r
101 public void AppendJoin(SqlStatement sqlStatement, IList<SqlStatement> sqlStatements)
\r
103 for (int index = 0; index < sqlStatements.Count; index++)
\r
106 Append(sqlStatement);
\r
107 Append(sqlStatements[index]);
\r
112 /// Creates an SQL statement based on a format string and SqlStatements as arguments
\r
114 /// <param name="format"></param>
\r
115 /// <param name="sqlStatements"></param>
\r
116 /// <returns></returns>
\r
117 public void AppendFormat(string format, IList<SqlStatement> sqlStatements)
\r
119 var statements = new ArrayList { format };
\r
120 // the strategy divides each part containing the {0}, {1}, etc
\r
121 // and inserts the required argument
\r
122 for (int index = 0; index < sqlStatements.Count; index++)
\r
124 var newStatements = new ArrayList();
\r
125 var literalIndex = "{" + index + "}";
\r
126 // then in each statement we look for the current literalIndex
\r
127 foreach (var statement in statements)
\r
129 // if we have a string, we split it around the literalIndex
\r
130 // and insert the SqlStatement between new parts
\r
131 var stringStatement = statement as string;
\r
132 if (stringStatement != null)
\r
134 var parts = stringStatement.Split(new[] { literalIndex }, StringSplitOptions.None);
\r
135 for (int partIndex = 0; partIndex < parts.Length; partIndex++)
\r
138 newStatements.Add(sqlStatements[index]);
\r
139 newStatements.Add(parts[partIndex]);
\r
142 else // no match found? add the raw statement
\r
143 newStatements.Add(statement);
\r
145 statements = newStatements;
\r
147 // finally, convert all remaining strings to SqlStatements
\r
148 foreach (var statement in statements)
\r
150 var stringStatement = statement as string;
\r
151 if (stringStatement != null)
\r
152 Append(new SqlStatement(stringStatement));
\r
154 Append((SqlStatement)statement);
\r
159 /// Formats an SqlStatement from a given string format
\r
161 /// <param name="format"></param>
\r
162 /// <param name="sqlStatements"></param>
\r
163 /// <returns></returns>
\r
164 public void AppendFormat(string format, params SqlStatement[] sqlStatements)
\r
166 AppendFormat(format, (IList<SqlStatement>)sqlStatements);
\r
170 /// Appends a bunch of sqlStatements to the current one
\r
172 /// <param name="sqlStatements"></param>
\r
173 /// <returns></returns>
\r
174 public void Append(IList<SqlStatement> sqlStatements)
\r
176 foreach (var sqlStatement in sqlStatements)
\r
178 foreach (var sqlPart in sqlStatement)
\r
180 AddPart(Parts, sqlPart);
\r
188 /// Inserts statements at given position
\r
190 /// <param name="index"></param>
\r
191 /// <param name="sqlStatements"></param>
\r
192 public void Insert(int index, IList<SqlStatement> sqlStatements)
\r
194 for (int statementIndex = sqlStatements.Count - 1; statementIndex >= 0; statementIndex--)
\r
196 var sqlStatement = sqlStatements[statementIndex];
\r
197 for (int partIndex = sqlStatement.Count - 1; partIndex >= 0; partIndex++)
\r
199 var sqlPart = sqlStatement[partIndex];
\r
200 InsertPart(Parts, index, sqlPart);
\r
206 /// Inserts statements at given position
\r
208 /// <param name="index"></param>
\r
209 /// <param name="sqlStatements"></param>
\r
210 public void Insert(int index, params SqlStatement[] sqlStatements)
\r
212 Insert(index, (IList<SqlStatement>)sqlStatements);
\r
218 /// Appends sqlStatements to the current one
\r
220 /// <param name="newStatements"></param>
\r
221 /// <returns></returns>
\r
222 public void Append(params SqlStatement[] newStatements)
\r
224 Append((IList<SqlStatement>)newStatements);
\r
228 /// Replaces the specified text, optionally ignoring the case.
\r
229 /// The method does not replace cross-parts text
\r
231 /// <param name="oldText">The old text.</param>
\r
232 /// <param name="newText">The new text.</param>
\r
233 /// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
\r
234 public void Replace(string oldText, string newText, bool ignoreCase)
\r
236 for (int partIndex = 0; partIndex < Parts.Count; partIndex++)
\r
238 var part = Parts[partIndex];
\r
239 if (part.Sql.ContainsCase(oldText, ignoreCase))
\r
241 // we know how to process only on literal strings
\r
242 if (part is SqlLiteralPart)
\r
244 Parts[partIndex] = new SqlLiteralPart(part.Sql.ReplaceCase(oldText, newText, ignoreCase));
\r
251 /// Initializes a new instance of the <see cref="SqlStatementBuilder"/> class.
\r
253 public SqlStatementBuilder()
\r
258 /// Initializes a new instance of the <see cref="SqlStatementBuilder"/> class.
\r
260 /// <param name="sqlStatements">The SQL statements.</param>
\r
261 public SqlStatementBuilder(params SqlStatement[] sqlStatements)
\r
263 Append(sqlStatements);
\r
267 /// Gets the built SqlStatement.
\r
269 /// <returns></returns>
\r
270 public SqlStatement ToSqlStatement()
\r
272 return new SqlStatement(Parts);
\r