| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Data.Linq.Mapping;
- using System.Data.Linq.Provider;
- using System.Data.SqlClient;
- using System.Globalization;
- using System.Linq;
- using System.Text;
- using System.Text.RegularExpressions;
- namespace System.Data.Linq.SqlClient {
- internal static class SqlIdentifier
- {
- private static SqlCommandBuilder builder = new SqlCommandBuilder();
- const string ParameterPrefix = "@";
- const string QuotePrefix = "[";
- const string QuoteSuffix = "]";
- const string SchemaSeparator = ".";
- const char SchemaSeparatorChar = '.';
- private static bool IsQuoted(string s) {
- if (s == null) {
- throw Error.ArgumentNull("s");
- }
- if (s.Length < 2) {
- return false;
- }
- return s.StartsWith(QuotePrefix, StringComparison.Ordinal)
- && s.EndsWith(QuoteSuffix, StringComparison.Ordinal);
- }
- // This is MSSQL-specific quoting.
- // If the string begins and ends with [ and ], it will be assumed to already be quoted.
- // Otherwise periods are assumed to be namespace delimiters, and the string is split on each.
- // Each string from the split is then check to see if it is already quoted, and if
- // not, it is replaced with the result of SqlCommandBuilder.QuoteIdentifier.
- // Then the set of strings is rejoined with periods.
- internal static string QuoteCompoundIdentifier(string s) {
- if (s == null) {
- throw Error.ArgumentNull("s");
- }
- // if it starts with @, then return unprocessed
- if (s.StartsWith(ParameterPrefix, StringComparison.Ordinal)) {
- return s;
- } else if (IsQuoted(s)) {
- return s;
- }
- else if (!s.StartsWith(QuotePrefix, StringComparison.Ordinal) && s.EndsWith(QuoteSuffix, StringComparison.Ordinal)) {
- //A.[B] => [A].[B]
- int splitPosition = s.IndexOf(SchemaSeparatorChar);
- if (splitPosition < 0){ //no . in the string
- return builder.QuoteIdentifier(s);
- }
- string left = s.Substring(0, splitPosition);
- string right = s.Substring(splitPosition + 1, s.Length - splitPosition - 1);
- if (!IsQuoted(right)) {
- right = builder.QuoteIdentifier(right);
- }
- return String.Concat(QuoteCompoundIdentifier(left), SchemaSeparatorChar + right);
- }
- else if (s.StartsWith(QuotePrefix, StringComparison.Ordinal) && !s.EndsWith(QuoteSuffix, StringComparison.Ordinal)) {
- //[A].B => [A].[B]
- int splitPosition = s.LastIndexOf(SchemaSeparatorChar);
- if (splitPosition < 0){ //no . in the string
- return builder.QuoteIdentifier(s);
- }
- string left = s.Substring(0, splitPosition);
- if (!IsQuoted(left)) {
- left = builder.QuoteIdentifier(left);
- }
- string right = s.Substring(splitPosition + 1, s.Length - splitPosition - 1);
- return String.Concat(left + SchemaSeparatorChar, QuoteCompoundIdentifier(right));
- }
- else {
- int splitPosition = s.IndexOf(SchemaSeparatorChar);
- if (splitPosition < 0) { //no . in the string
- //A => [A]
- return builder.QuoteIdentifier(s);
- }
- string left = s.Substring(0, splitPosition);
- string right = s.Substring(splitPosition + 1, s.Length - splitPosition - 1);
- return String.Concat(QuoteCompoundIdentifier(left) + SchemaSeparatorChar, QuoteCompoundIdentifier(right));
- }
- }
- // This is MSSQL-specific quoting.
- // This is the same as above, but it doesn't consider anything compound.
- internal static string QuoteIdentifier(string s) {
- if (s == null) {
- throw Error.ArgumentNull("s");
- }
- // if it starts with @, then return unprocessed
- if (s.StartsWith(ParameterPrefix, StringComparison.Ordinal)) {
- return s;
- } else if (IsQuoted(s)) {
- return s;
- } else {
- return builder.QuoteIdentifier(s);
- }
- }
- // turns "[ABC].[PQR].[XYZ]" into {"[ABC]", "[PQR]", "[XYZ]"}
- internal static IEnumerable<string> GetCompoundIdentifierParts(string s) {
- if (s == null) {
- throw Error.ArgumentNull("s");
- }
- // can't do this to parameters
- if (s.StartsWith(ParameterPrefix, StringComparison.Ordinal)) {
- throw Error.ArgumentWrongValue("s");
- }
- string quotedS = QuoteCompoundIdentifier(s);
- string pattern = @"^(?<component>\[([^\]]|\]\])*\])(\.(?<component>\[([^\]]|\]\])*\]))*$";
- // This pattern matches "."-delimited quoted SQL identifiers. Here's how:
- //
- // 1. It is wrapped in "^" and "$", which match the begining and end of text, so it will match
- // only the entire text and not any sub-part.
- // 2. The group "(?<component>\[([^\]]|\]\])*\])" captures a single quoted segment of the text.
- // It's a literal "[" followed by any number of non-"]" characters or "]]" strings, followed
- // by a literal "]". The "?<component>" bit names the capture so we can refer to it.
- // 3. After the first component, we will allow any number of groups which consist of a literal
- // "." followed by a component (and the component part is a repeat of the part described in 2).
-
- Match m = Regex.Match(quotedS, pattern);
- if (!m.Success)
- {
- throw Error.ArgumentWrongValue("s");
- }
- foreach (Capture cap in m.Groups["component"].Captures)
- {
- yield return cap.Value;
- }
- }
- }
- }
|