View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.db.protocol.postgresql.packet.generic;
19  
20  import com.google.common.base.Preconditions;
21  import com.google.common.base.Strings;
22  import org.apache.shardingsphere.infra.exception.postgresql.vendor.PostgreSQLVendorError;
23  import org.apache.shardingsphere.db.protocol.postgresql.packet.identifier.PostgreSQLIdentifierPacket;
24  import org.apache.shardingsphere.db.protocol.postgresql.packet.identifier.PostgreSQLIdentifierTag;
25  import org.apache.shardingsphere.db.protocol.postgresql.packet.identifier.PostgreSQLMessagePacketType;
26  import org.apache.shardingsphere.db.protocol.postgresql.payload.PostgreSQLPacketPayload;
27  
28  import java.util.LinkedHashMap;
29  import java.util.Map;
30  import java.util.Map.Entry;
31  import java.util.stream.Collectors;
32  
33  /**
34   * Error response packet for PostgreSQL.
35   * 
36   * @see <a href="https://www.postgresql.org/docs/12/protocol-message-formats.html">ErrorResponse (B)</a>
37   */
38  public final class PostgreSQLErrorResponsePacket extends PostgreSQLIdentifierPacket {
39      
40      public static final char FIELD_TYPE_SEVERITY = 'S';
41      
42      public static final char FIELD_TYPE_SEVERITY_NON_LOCALIZED = 'V';
43      
44      public static final char FIELD_TYPE_CODE = 'C';
45      
46      public static final char FIELD_TYPE_MESSAGE = 'M';
47      
48      public static final char FIELD_TYPE_DETAIL = 'D';
49      
50      public static final char FIELD_TYPE_HINT = 'H';
51      
52      public static final char FIELD_TYPE_POSITION = 'P';
53      
54      public static final char FIELD_TYPE_INTERNAL_POSITION = 'p';
55      
56      public static final char FIELD_TYPE_INTERNAL_QUERY = 'q';
57      
58      public static final char FIELD_TYPE_WHERE = 'W';
59      
60      public static final char FIELD_TYPE_SCHEMA_NAME = 's';
61      
62      public static final char FIELD_TYPE_TABLE_NAME = 't';
63      
64      public static final char FIELD_TYPE_COLUMN_NAME = 'c';
65      
66      public static final char FIELD_TYPE_DATA_TYPE_NAME = 'd';
67      
68      public static final char FIELD_TYPE_CONSTRAINT_NAME = 'n';
69      
70      public static final char FIELD_TYPE_FILE = 'F';
71      
72      public static final char FIELD_TYPE_LINE = 'L';
73      
74      public static final char FIELD_TYPE_ROUTINE = 'R';
75      
76      private final Map<Character, String> fields = new LinkedHashMap<>(16, 1F);
77      
78      private PostgreSQLErrorResponsePacket(final Map<Character, String> fields) {
79          this.fields.putAll(fields);
80      }
81      
82      /**
83       * To server error message.
84       *
85       * @return server error message
86       */
87      public String toServerErrorMessage() {
88          return fields.entrySet().stream().map(entry -> entry.getKey() + entry.getValue()).collect(Collectors.joining("\0"));
89      }
90      
91      @Override
92      protected void write(final PostgreSQLPacketPayload payload) {
93          for (Entry<Character, String> entry : fields.entrySet()) {
94              payload.writeInt1(entry.getKey());
95              payload.writeStringNul(entry.getValue());
96          }
97          payload.writeInt1(0);
98      }
99      
100     @Override
101     public PostgreSQLIdentifierTag getIdentifier() {
102         return PostgreSQLMessagePacketType.ERROR_RESPONSE;
103     }
104     
105     /**
106      * Create PostgreSQL error response packet builder with required arguments.
107      *
108      * @param severity severity
109      * @param vendorError PostgreSQL vendor error
110      * @param message message
111      * @return PostgreSQL error response packet builder
112      * @see <a href="https://www.postgresql.org/docs/12/protocol-error-fields.html">52.8. Error and Notice Message Fields</a>
113      */
114     public static Builder newBuilder(final String severity, final PostgreSQLVendorError vendorError, final String message) {
115         return newBuilder(severity, vendorError.getSqlState().getValue(), message);
116     }
117     
118     /**
119      * Create PostgreSQL error response packet builder with required arguments.
120      *
121      * @param severity severity
122      * @param sqlState SQL state
123      * @param message message
124      * @return PostgreSQL error response packet builder
125      * @see <a href="https://www.postgresql.org/docs/12/protocol-error-fields.html">52.8. Error and Notice Message Fields</a>
126      */
127     public static Builder newBuilder(final String severity, final String sqlState, final String message) {
128         return new Builder(severity, sqlState, message);
129     }
130     
131     public static final class Builder {
132         
133         private final Map<Character, String> fields = new LinkedHashMap<>(16, 1F);
134         
135         private Builder(final String severity, final String sqlState, final String message) {
136             Preconditions.checkArgument(null != severity, "The severity is always present!");
137             Preconditions.checkArgument(!Strings.isNullOrEmpty(sqlState), "The SQLSTATE code is always present!");
138             Preconditions.checkArgument(!Strings.isNullOrEmpty(message), "The message is always present!");
139             fields.put(FIELD_TYPE_SEVERITY, severity);
140             fields.put(FIELD_TYPE_SEVERITY_NON_LOCALIZED, severity);
141             fields.put(FIELD_TYPE_CODE, sqlState);
142             fields.put(FIELD_TYPE_MESSAGE, message);
143         }
144         
145         /**
146          * Set detail.
147          *
148          * @param detail detail
149          * @return PostgreSQL error response packet builder
150          */
151         public Builder detail(final String detail) {
152             if (!Strings.isNullOrEmpty(detail)) {
153                 fields.put(FIELD_TYPE_DETAIL, detail);
154             }
155             return this;
156         }
157         
158         /**
159          * Set hint.
160          *
161          * @param hint hint
162          * @return PostgreSQL error response packet builder
163          */
164         public Builder hint(final String hint) {
165             if (!Strings.isNullOrEmpty(hint)) {
166                 fields.put(FIELD_TYPE_HINT, hint);
167             }
168             return this;
169         }
170         
171         /**
172          * Set position. The first character has index 1, and positions are measured in characters not bytes.
173          *
174          * @param position position
175          * @return PostgreSQL error response packet builder
176          */
177         public Builder position(final int position) {
178             if (position > 0) {
179                 fields.put(FIELD_TYPE_POSITION, Integer.toString(position));
180             }
181             return this;
182         }
183         
184         /**
185          * Set internal query and internal position. The first character has index 1, and positions are measured in characters not bytes.
186          *
187          * @param internalQuery internal query
188          * @param internalPosition internal position
189          * @return PostgreSQL error response packet builder
190          */
191         public Builder internalQueryAndInternalPosition(final String internalQuery, final int internalPosition) {
192             if (internalPosition > 0) {
193                 fields.put(FIELD_TYPE_INTERNAL_POSITION, Integer.toString(internalPosition));
194             }
195             return internalQuery(internalQuery);
196         }
197         
198         /**
199          * Set internal query.
200          *
201          * @param internalQuery internal query
202          * @return PostgreSQL error response packet builder
203          */
204         public Builder internalQuery(final String internalQuery) {
205             if (!Strings.isNullOrEmpty(internalQuery)) {
206                 fields.put(FIELD_TYPE_INTERNAL_QUERY, internalQuery);
207             }
208             return this;
209         }
210         
211         /**
212          * Set where.
213          *
214          * @param where where
215          * @return PostgreSQL error response packet builder
216          */
217         public Builder where(final String where) {
218             if (!Strings.isNullOrEmpty(where)) {
219                 fields.put(FIELD_TYPE_WHERE, where);
220             }
221             return this;
222         }
223         
224         /**
225          * Set schema name.
226          *
227          * @param schemaName schema name
228          * @return PostgreSQL error response packet builder
229          */
230         public Builder schemaName(final String schemaName) {
231             if (!Strings.isNullOrEmpty(schemaName)) {
232                 fields.put(FIELD_TYPE_SCHEMA_NAME, schemaName);
233             }
234             return this;
235         }
236         
237         /**
238          * Set table name.
239          *
240          * @param tableName table name
241          * @return PostgreSQL error response packet builder
242          */
243         public Builder tableName(final String tableName) {
244             if (!Strings.isNullOrEmpty(tableName)) {
245                 fields.put(FIELD_TYPE_TABLE_NAME, tableName);
246             }
247             return this;
248         }
249         
250         /**
251          * Set column name.
252          *
253          * @param columnName column name
254          * @return PostgreSQL error response packet builder
255          */
256         public Builder columnName(final String columnName) {
257             if (!Strings.isNullOrEmpty(columnName)) {
258                 fields.put(FIELD_TYPE_COLUMN_NAME, columnName);
259             }
260             return this;
261         }
262         
263         /**
264          * Set data type name.
265          *
266          * @param dataTypeName data type name
267          * @return PostgreSQL error response packet builder
268          */
269         public Builder dataTypeName(final String dataTypeName) {
270             if (!Strings.isNullOrEmpty(dataTypeName)) {
271                 fields.put(FIELD_TYPE_DATA_TYPE_NAME, dataTypeName);
272             }
273             return this;
274         }
275         
276         /**
277          * Set constraint name.
278          *
279          * @param constraintName constraint name
280          * @return PostgreSQL error response packet builder
281          */
282         public Builder constraintName(final String constraintName) {
283             if (!Strings.isNullOrEmpty(constraintName)) {
284                 fields.put(FIELD_TYPE_CONSTRAINT_NAME, constraintName);
285             }
286             return this;
287         }
288         
289         /**
290          * Set file.
291          *
292          * @param file file
293          * @return PostgreSQL error response packet builder
294          */
295         public Builder file(final String file) {
296             if (!Strings.isNullOrEmpty(file)) {
297                 fields.put(FIELD_TYPE_FILE, file);
298             }
299             return this;
300         }
301         
302         /**
303          * Set line.
304          *
305          * @param line line
306          * @return PostgreSQL error response packet builder
307          */
308         public Builder line(final int line) {
309             if (line > 0) {
310                 fields.put(FIELD_TYPE_LINE, Integer.toString(line));
311             }
312             return this;
313         }
314         
315         /**
316          * Set routine.
317          *
318          * @param routine routine
319          * @return PostgreSQL error response packet builder
320          */
321         public Builder routine(final String routine) {
322             if (!Strings.isNullOrEmpty(routine)) {
323                 fields.put(FIELD_TYPE_ROUTINE, routine);
324             }
325             return this;
326         }
327         
328         /**
329          * Build PostgreSQL error response packet builder.
330          *
331          * @return PostgreSQL error response packet builder
332          */
333         public PostgreSQLErrorResponsePacket build() {
334             return new PostgreSQLErrorResponsePacket(fields);
335         }
336     }
337 }