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 }