1 /*
2  * Copyright (c) 2004, PostgreSQL Global Development Group
3  * See the LICENSE file in the project root for more information.
4  */
5 // Copyright (c) 2004, Open Cloud Limited.
6 
7 
8 module hunt.database.driver.postgresql.PgUtil;
9 
10 import hunt.database.base.Exceptions;
11 import hunt.Exceptions;
12 import hunt.util.Appendable;
13 import hunt.util.Common;
14 import hunt.util.StringBuilder;
15 
16 /**
17  * Collection of utilities used by the protocol-level code.
18  * 
19  * Ported from org.postgresql.core.Utils
20  */
21 class PgUtil {
22 
23     static string escapeWithQuotes(string value) {
24         scope StringBuilder sb = new StringBuilder((cast(int)value.length + 10) / 10 * 11); // Add 10% for escaping.
25         sb.append('\'') ;
26         escapeLiteral(sb, value, false);
27         sb.append('\'') ;
28 
29         return sb.toString();
30     }
31 
32     /**
33      * Escape the given literal <tt>value</tt> and append it to the string builder <tt>sbuf</tt>. If
34      * <tt>sbuf</tt> is <tt>null</tt>, a new StringBuilder will be returned. The argument
35      * <tt>standardConformingStrings</tt> defines whether the backend expects standard-conforming
36      * string literals or allows backslash escape sequences.
37      *
38      * @param sbuf the string builder to append to; or <tt>null</tt>
39      * @param value the string value
40      * @param standardConformingStrings if standard conforming strings should be used
41      * @return the sbuf argument; or a new string builder for sbuf is null
42      * @throws SQLException if the string contains a <tt>\0</tt> character
43      */
44     static StringBuilder escapeLiteral(StringBuilder sbuf, string value, bool standardConformingStrings) {
45         if (sbuf is null) {
46             sbuf = new StringBuilder((cast(int)value.length + 10) / 10 * 11); // Add 10% for escaping.
47         }
48         doAppendEscapedLiteral(sbuf, value, standardConformingStrings);
49         return sbuf;
50     }
51 
52     /**
53      * Common part for {@link #escapeLiteral(StringBuilder, string, bool)}.
54      *
55      * @param sbuf Either StringBuffer or StringBuilder as we do not expect any IOException to be
56      *        thrown
57      * @param value value to append
58      * @param standardConformingStrings if standard conforming strings should be used
59      */
60     private static void doAppendEscapedLiteral(Appendable sbuf, string value,
61             bool standardConformingStrings) {
62         try {
63             if (standardConformingStrings) {
64                 // With standard_conforming_strings on, escape only single-quotes.
65                 for (size_t i = 0; i < value.length; ++i) {
66                     char ch = value[i];
67                     if (ch == '\0') {
68                         throw new DatabaseException("Zero bytes may not occur in string parameters.");
69                     }
70                     if (ch == '\'') {
71                         sbuf.append('\'');
72                     }
73                     sbuf.append(ch);
74                 }
75             } else {
76                 // With standard_conforming_string off, escape backslashes and
77                 // single-quotes, but still escape single-quotes by doubling, to
78                 // avoid a security hazard if the reported value of
79                 // standard_conforming_strings is incorrect, or an error if
80                 // backslash_quote is off.
81                 for (size_t i = 0; i < value.length; ++i) {
82                     char ch = value[i];
83                     if (ch == '\0') {
84                         throw new DatabaseException("Zero bytes may not occur in string parameters.");
85                     }
86                     if (ch == '\\' || ch == '\'') {
87                         sbuf.append(ch);
88                     }
89                     sbuf.append(ch);
90                 }
91             }
92         } catch (IOException e) {
93             throw new DatabaseException("No IOException expected from StringBuffer or StringBuilder", e);
94         }
95     }
96 
97     /**
98      * Escape the given identifier <tt>value</tt> and append it to the string builder <tt>sbuf</tt>.
99      * If <tt>sbuf</tt> is <tt>null</tt>, a new StringBuilder will be returned. This method is
100      * different from appendEscapedLiteral in that it includes the quoting required for the identifier
101      * while {@link #escapeLiteral(StringBuilder, string, bool)} does not.
102      *
103      * @param sbuf the string builder to append to; or <tt>null</tt>
104      * @param value the string value
105      * @return the sbuf argument; or a new string builder for sbuf is null
106      * @throws SQLException if the string contains a <tt>\0</tt> character
107      */
108     static StringBuilder escapeIdentifier(StringBuilder sbuf, string value) {
109         if (sbuf is null) {
110             sbuf = new StringBuilder(2 + (cast(int)value.length + 10) / 10 * 11); // Add 10% for escaping.
111         }
112         doAppendEscapedIdentifier(sbuf, value);
113         return sbuf;
114     }
115 
116     /**
117      * Common part for appendEscapedIdentifier.
118      *
119      * @param sbuf Either StringBuffer or StringBuilder as we do not expect any IOException to be
120      *        thrown.
121      * @param value value to append
122      */
123     private static void doAppendEscapedIdentifier(Appendable sbuf, string value) {
124         try {
125             sbuf.append('"');
126 
127             for (size_t i = 0; i < value.length; ++i) {
128                 char ch = value[i];
129                 if (ch == '\0') {
130                     throw new DatabaseException("Zero bytes may not occur in identifiers.");
131                 }
132                 if (ch == '"') {
133                     sbuf.append(ch);
134                 }
135                 sbuf.append(ch);
136             }
137 
138             sbuf.append('"');
139         } catch (IOException e) {
140             throw new DatabaseException("No IOException expected from StringBuffer or StringBuilder", e);
141         }
142     }
143 
144 }