Skip to content

Commit c5ec839

Browse files
committed
support pg binary protocol
1 parent 8bab753 commit c5ec839

File tree

67 files changed

+2122
-820
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2122
-820
lines changed

database/protocol/type/postgresql/src/main/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/PostgreSQLDataRowPacket.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ private void writeBinaryValue(final PostgreSQLPacketPayload payload, final Binar
5959
return;
6060
}
6161
PostgreSQLBinaryProtocolValue binaryProtocolValue = PostgreSQLBinaryProtocolValueFactory.getBinaryProtocolValue(each.getColumnType());
62-
payload.writeInt4(binaryProtocolValue.getColumnLength(payload, value));
62+
int columnLength = binaryProtocolValue.getColumnLength(payload, value);
63+
if (columnLength > 0) {
64+
payload.writeInt4(columnLength);
65+
}
6366
binaryProtocolValue.write(payload, value);
6467
}
6568

database/protocol/type/postgresql/src/main/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/PostgreSQLColumnType.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@ public enum PostgreSQLColumnType implements BinaryColumnType {
197197
JDBC_TYPE_AND_COLUMN_TYPE_MAP.put(Types.BOOLEAN, BOOL);
198198
// TODO Temporary solution for https://github.com/apache/shardingsphere/issues/22522
199199
JDBC_TYPE_AND_COLUMN_TYPE_MAP.put(Types.STRUCT, VARCHAR);
200-
JDBC_TYPE_AND_COLUMN_TYPE_MAP.put(Types.ARRAY, TEXT_ARRAY);
201200
}
202201

203202
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.apache.shardingsphere.database.protocol.postgresql.packet.command.query.extended.bind.protocol;
2+
3+
import org.apache.shardingsphere.database.protocol.postgresql.payload.PostgreSQLPacketPayload;
4+
import org.apache.shardingsphere.database.protocol.postgresql.packet.command.query.extended.bind.protocol.util.codec.decoder.PgBinaryObj;
5+
import org.postgresql.core.Oid;
6+
import org.postgresql.jdbc.ShardingSpherePgArrayUtils;
7+
8+
import java.nio.ByteBuffer;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
12+
public class PostgreSQLArrayBinaryProtocolValue implements PostgreSQLBinaryProtocolValue {
13+
14+
private static final Map<Integer, String> oidTypeName = new HashMap<>();
15+
16+
static {
17+
oidTypeName.put(Oid.BOOL, "bool[]");
18+
oidTypeName.put(Oid.BYTEA, "bytea[]");
19+
// oidTypeName.put(Oid.CHAR_ARRAY, "char[]");
20+
// oidTypeName.put(Oid.NAME_ARRAY, "name[]");
21+
oidTypeName.put(Oid.INT2, "int2[]");
22+
oidTypeName.put(Oid.INT4, "int4[]");
23+
oidTypeName.put(Oid.INT8, "int8[]");
24+
oidTypeName.put(Oid.FLOAT4, "float4[]");
25+
oidTypeName.put(Oid.FLOAT8, "float8[]");
26+
oidTypeName.put(Oid.TEXT, "text[]");
27+
oidTypeName.put(Oid.VARCHAR, "varchar[]");
28+
oidTypeName.put(Oid.DATE, "date[]");
29+
oidTypeName.put(Oid.TIMESTAMP, "timestamp[]");
30+
// oidTypeName.put(Oid.TIMESTAMPTZ_ARRAY, "timestamptz[]");
31+
oidTypeName.put(Oid.TIME, "time[]");
32+
// oidTypeName.put(Oid.TIMETZ_ARRAY, "timetz[]");
33+
oidTypeName.put(Oid.NUMERIC, "numeric[]");
34+
// oidTypeName.put(Oid.UUID_ARRAY, "uuid[]");
35+
}
36+
37+
private PostgreSQLArrayBinaryProtocolValue(){
38+
39+
}
40+
public static final PostgreSQLArrayBinaryProtocolValue instance = new PostgreSQLArrayBinaryProtocolValue();
41+
42+
@Override
43+
public int getColumnLength(PostgreSQLPacketPayload payload, Object value) {
44+
return -1;
45+
}
46+
47+
@Override
48+
public Object read(PostgreSQLPacketPayload payload, int parameterValueLength) {
49+
byte[] bytes = new byte[parameterValueLength];
50+
payload.getByteBuf().readBytes(bytes);
51+
ByteBuffer buf = ByteBuffer.wrap(bytes);
52+
int oid = buf.getInt(8);
53+
String typeName = oidTypeName.get(oid);
54+
PgBinaryObj pgBinaryObj = new PgBinaryObj(bytes);
55+
pgBinaryObj.setType(typeName);
56+
return pgBinaryObj;
57+
}
58+
59+
@Override
60+
public void write(PostgreSQLPacketPayload payload, Object value) {
61+
try {
62+
byte[] result = ShardingSpherePgArrayUtils.getBinaryBytes(value,payload.getCharset());
63+
payload.writeInt4(result.length);
64+
payload.writeBytes(result);
65+
}catch (Exception e) {
66+
e.printStackTrace();
67+
}
68+
69+
}
70+
}

database/protocol/type/postgresql/src/main/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLArrayParameterDecoder.java

Lines changed: 206 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@
2020
import com.google.common.base.Preconditions;
2121
import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
2222
import org.apache.shardingsphere.infra.exception.generic.UnsupportedSQLOperationException;
23+
import org.checkerframework.checker.nullness.qual.Nullable;
24+
import org.postgresql.core.Parser;
2325

26+
27+
import java.lang.reflect.Array;
28+
import java.math.BigDecimal;
2429
import java.nio.charset.StandardCharsets;
25-
import java.util.Arrays;
26-
import java.util.Collection;
30+
import java.util.*;
2731
import java.util.stream.Collectors;
2832

33+
import static org.postgresql.util.internal.Nullness.castNonNull;
34+
2935
/**
3036
* PostgreSQL array parameter decoder.
3137
*/
@@ -187,4 +193,202 @@ private static String decodeElementText(final String element) {
187193
}
188194
return result;
189195
}
196+
197+
198+
199+
public Object decodeNumberArray(String parameterValue) {
200+
201+
PgDimensionsArrayList list = decodeFromString(parameterValue, ',');
202+
int dims = list.dimensionsCount;
203+
final int[] dimensionLengths = new int[dims];
204+
dimensionLengths[0] = list.size();
205+
206+
// find first non-null list
207+
208+
for (int i = 1; i < dims; i++) {
209+
List tmpList = (List) list.get(0);
210+
dimensionLengths[i] = castNonNull(tmpList, "first element of adjustedList is null").size();
211+
if (i != dims - 1) {
212+
tmpList = (List) tmpList.get(0);
213+
}
214+
}
215+
Object[] array = (Object[]) Array.newInstance(Number.class, dimensionLengths);
216+
if (array instanceof Number[]){
217+
parserNumber((Number[]) array,list);
218+
}else {
219+
storeStringValues(array,list,dimensionLengths,0);
220+
}
221+
return array;
222+
}
223+
224+
private static void storeStringValues(Object[] array, List list, int [] dimensionLengths,
225+
int dim) {
226+
227+
for (int i = 0; i < dimensionLengths[dim]; i++) {
228+
Object element = castNonNull(list.get(i), "list.get(i)");
229+
if (dim == dimensionLengths.length - 2) {
230+
parserNumber((Number[]) array[i],(List) element);
231+
} else {
232+
storeStringValues((Object[]) array[i], (List) element, dimensionLengths, dim + 1);
233+
}
234+
}
235+
}
236+
237+
private static void parserNumber(Number[] target, List source) {
238+
for (int i = 0; i < target.length; i++) {
239+
Object o = source.get(i);
240+
if (o== null){
241+
continue;
242+
}
243+
target[i] = parseNumber(o.toString());
244+
}
245+
}
246+
247+
248+
private static Number parseNumber(String each) {
249+
if (each.startsWith("\"") && each.endsWith("\"") && each.length() > 2) {
250+
each = each.substring(1, each.length() - 1);
251+
}
252+
if (Double.toString(Double.NaN).equals(each)) {
253+
return Double.NaN;
254+
}
255+
if (Double.toString(Double.POSITIVE_INFINITY).equals(each)) {
256+
return Double.POSITIVE_INFINITY;
257+
}
258+
if (Double.toString(Double.NEGATIVE_INFINITY).equals(each)) {
259+
return Double.POSITIVE_INFINITY;
260+
}
261+
return new BigDecimal(each);
262+
}
263+
264+
static final class PgDimensionsArrayList extends ArrayList<@Nullable Object> {
265+
266+
private static final long serialVersionUID = 1L;
267+
268+
/**
269+
* How many dimensions.
270+
*/
271+
int dimensionsCount = 1;
272+
273+
}
274+
275+
public PgDimensionsArrayList decodeFromString(String fieldString, char delim) {
276+
277+
final PgDimensionsArrayList arrayList = new PgDimensionsArrayList();
278+
279+
if (fieldString == null) {
280+
return arrayList;
281+
}
282+
283+
final char[] chars = fieldString.toCharArray();
284+
StringBuilder buffer = null;
285+
boolean insideString = false;
286+
287+
// needed for checking if NULL value occurred
288+
boolean wasInsideString = false;
289+
290+
// array dimension arrays
291+
final List<PgDimensionsArrayList> dims = new ArrayList<>();
292+
293+
// currently processed array
294+
PgDimensionsArrayList curArray = arrayList;
295+
296+
// Starting with 8.0 non-standard (beginning index
297+
// isn't 1) bounds the dimensions are returned in the
298+
// data formatted like so "[0:3]={0,1,2,3,4}".
299+
// Older versions simply do not return the bounds.
300+
//
301+
// Right now we ignore these bounds, but we could
302+
// consider allowing these index values to be used
303+
// even though the JDBC spec says 1 is the first
304+
// index. I'm not sure what a client would like
305+
// to see, so we just retain the old behavior.
306+
int startOffset = 0;
307+
{
308+
if (chars[0] == '[') {
309+
while (chars[startOffset] != '=') {
310+
startOffset++;
311+
}
312+
startOffset++; // skip =
313+
}
314+
}
315+
316+
for (int i = startOffset; i < chars.length; i++) {
317+
318+
// escape character that we need to skip
319+
if (chars[i] == '\\') {
320+
i++;
321+
} else if (!insideString && chars[i] == '{') {
322+
// subarray start
323+
if (dims.isEmpty()) {
324+
dims.add(arrayList);
325+
} else {
326+
PgDimensionsArrayList a = new PgDimensionsArrayList();
327+
PgDimensionsArrayList p = dims.get(dims.size() - 1);
328+
p.add(a);
329+
dims.add(a);
330+
}
331+
curArray = dims.get(dims.size() - 1);
332+
333+
// number of dimensions
334+
{
335+
for (int t = i + 1; t < chars.length; t++) {
336+
if (Character.isWhitespace(chars[t])) {
337+
continue;
338+
} else if (chars[t] == '{') {
339+
curArray.dimensionsCount++;
340+
} else {
341+
break;
342+
}
343+
}
344+
}
345+
346+
buffer = new StringBuilder();
347+
continue;
348+
} else if (chars[i] == '"') {
349+
// quoted element
350+
insideString = !insideString;
351+
wasInsideString = true;
352+
continue;
353+
} else if (!insideString && Parser.isArrayWhiteSpace(chars[i])) {
354+
// white space
355+
continue;
356+
} else if ((!insideString && (chars[i] == delim || chars[i] == '}')) || i == chars.length - 1) {
357+
// array end or element end
358+
// when character that is a part of array element
359+
if (chars[i] != '"' && chars[i] != '}' && chars[i] != delim && buffer != null) {
360+
buffer.append(chars[i]);
361+
}
362+
363+
String b = buffer == null ? null : buffer.toString();
364+
365+
// add element to current array
366+
if (b != null && (!b.isEmpty() || wasInsideString)) {
367+
curArray.add(!wasInsideString && "NULL".equals(b) ? null : b);
368+
}
369+
370+
wasInsideString = false;
371+
buffer = new StringBuilder();
372+
373+
// when end of an array
374+
if (chars[i] == '}') {
375+
dims.remove(dims.size() - 1);
376+
377+
// when multi-dimension
378+
if (!dims.isEmpty()) {
379+
curArray = dims.get(dims.size() - 1);
380+
}
381+
382+
buffer = null;
383+
}
384+
385+
continue;
386+
}
387+
388+
if (buffer != null) {
389+
buffer.append(chars[i]);
390+
}
391+
}
392+
return arrayList;
393+
}
190394
}

database/protocol/type/postgresql/src/main/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLBinaryProtocolValue.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public interface PostgreSQLBinaryProtocolValue {
2626

2727
/**
2828
* Get column length.
29+
* return -1 if we cant get column length quickly
2930
*
3031
* @param payload payload operation for PostgreSQL packet
3132
* @param value value of column

0 commit comments

Comments
 (0)