File: SqlIdentifier.cs

package info (click to toggle)
mono 6.12.0.199%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,296,836 kB
  • sloc: cs: 11,181,803; xml: 2,850,076; ansic: 699,709; cpp: 123,344; perl: 59,361; javascript: 30,841; asm: 21,853; makefile: 20,405; sh: 15,009; python: 4,839; pascal: 925; sql: 859; sed: 16; php: 1
file content (145 lines) | stat: -rw-r--r-- 6,255 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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;
            }
        }
    }
}