/* * Copyright (c) 2004, PostgreSQL Global Development Group * See the LICENSE file in the project root for more information. */ package com.amazon.redshift.jdbc; import com.amazon.redshift.core.Oid; import com.amazon.redshift.core.TypeInfo; import com.amazon.redshift.util.ByteConverter; import java.sql.Connection; import java.sql.SQLFeatureNotSupportedException; import java.util.HashMap; import java.util.Map; abstract class PrimitiveArraySupport { public abstract int getDefaultArrayTypeOid(TypeInfo tiCache); public abstract String toArrayString(char delim, A array); public abstract void appendArray(StringBuilder sb, char delim, A array); public boolean supportBinaryRepresentation() { return true; } public abstract byte[] toBinaryRepresentation(Connection connection, A array) throws SQLFeatureNotSupportedException; private static final PrimitiveArraySupport LONG_ARRAY = new PrimitiveArraySupport() { /** * {@inheritDoc} */ @Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.INT8_ARRAY; } @Override public String toArrayString(char delim, long[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); } /** * {@inheritDoc} */ @Override public void appendArray(StringBuilder sb, char delim, long[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(array[i]); } sb.append('}'); } /** * {@inheritDoc} */ @Override public byte[] toBinaryRepresentation(Connection connection, long[] array) { int length = 20 + (12 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.INT8); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 8; ByteConverter.int8(bytes, idx + 4, array[i]); idx += 12; } return bytes; } }; private static final PrimitiveArraySupport INT_ARRAY = new PrimitiveArraySupport() { /** * {@inheritDoc} */ @Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.INT4_ARRAY; } @Override public String toArrayString(char delim, int[] array) { final StringBuilder sb = new StringBuilder(Math.max(32, array.length * 6)); appendArray(sb, delim, array); return sb.toString(); } /** * {@inheritDoc} */ @Override public void appendArray(StringBuilder sb, char delim, int[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(array[i]); } sb.append('}'); } /** * {@inheritDoc} */ @Override public byte[] toBinaryRepresentation(Connection connection, int[] array) { int length = 20 + (8 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.INT4); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 4; ByteConverter.int4(bytes, idx + 4, array[i]); idx += 8; } return bytes; } }; private static final PrimitiveArraySupport SHORT_ARRAY = new PrimitiveArraySupport() { /** * {@inheritDoc} */ @Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.INT2_ARRAY; } @Override public String toArrayString(char delim, short[] array) { final StringBuilder sb = new StringBuilder(Math.max(32, array.length * 4)); appendArray(sb, delim, array); return sb.toString(); } /** * {@inheritDoc} */ @Override public void appendArray(StringBuilder sb, char delim, short[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(array[i]); } sb.append('}'); } /** * {@inheritDoc} */ @Override public byte[] toBinaryRepresentation(Connection connection, short[] array) { int length = 20 + (6 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.INT2); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 2; ByteConverter.int2(bytes, idx + 4, array[i]); idx += 6; } return bytes; } }; private static final PrimitiveArraySupport DOUBLE_ARRAY = new PrimitiveArraySupport() { /** * {@inheritDoc} */ @Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.FLOAT8_ARRAY; } @Override public String toArrayString(char delim, double[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); } /** * {@inheritDoc} */ @Override public void appendArray(StringBuilder sb, char delim, double[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } // use quotes to account for any issues with scientific notation sb.append('"'); sb.append(array[i]); sb.append('"'); } sb.append('}'); } /** * {@inheritDoc} */ @Override public byte[] toBinaryRepresentation(Connection connection, double[] array) { int length = 20 + (12 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.FLOAT8); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 8; ByteConverter.float8(bytes, idx + 4, array[i]); idx += 12; } return bytes; } }; private static final PrimitiveArraySupport FLOAT_ARRAY = new PrimitiveArraySupport() { /** * {@inheritDoc} */ @Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.FLOAT4_ARRAY; } @Override public String toArrayString(char delim, float[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); } /** * {@inheritDoc} */ @Override public void appendArray(StringBuilder sb, char delim, float[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } // use quotes to account for any issues with scientific notation sb.append('"'); sb.append(array[i]); sb.append('"'); } sb.append('}'); } /** * {@inheritDoc} */ @Override public byte[] toBinaryRepresentation(Connection connection, float[] array) { int length = 20 + (8 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.FLOAT4); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 4; ByteConverter.float4(bytes, idx + 4, array[i]); idx += 8; } return bytes; } }; private static final PrimitiveArraySupport BOOLEAN_ARRAY = new PrimitiveArraySupport() { /** * {@inheritDoc} */ @Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.BOOL_ARRAY; } @Override public String toArrayString(char delim, boolean[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); } /** * {@inheritDoc} */ @Override public void appendArray(StringBuilder sb, char delim, boolean[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(array[i] ? '1' : '0'); } sb.append('}'); } /** * {@inheritDoc} * * @throws SQLFeatureNotSupportedException * Because this feature is not supported. */ @Override public byte[] toBinaryRepresentation(Connection connection, boolean[] array) throws SQLFeatureNotSupportedException { int length = 20 + (5 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.BOOL); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 1; ByteConverter.bool(bytes, idx + 4, array[i]); idx += 5; } return bytes; } }; private static final PrimitiveArraySupport STRING_ARRAY = new PrimitiveArraySupport() { /** * {@inheritDoc} */ @Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.VARCHAR_ARRAY; } @Override public String toArrayString(char delim, String[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); } /** * {@inheritDoc} */ @Override public void appendArray(StringBuilder sb, char delim, String[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } if (array[i] == null) { sb.append('N'); sb.append('U'); sb.append('L'); sb.append('L'); } else { RedshiftArray.escapeArrayElement(sb, array[i]); } } sb.append('}'); } /** * {@inheritDoc} */ @Override public boolean supportBinaryRepresentation() { return false; } /** * {@inheritDoc} * * @throws SQLFeatureNotSupportedException * Because this feature is not supported. */ @Override public byte[] toBinaryRepresentation(Connection connection, String[] array) throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException(); } }; private static final Map ARRAY_CLASS_TO_SUPPORT = new HashMap((int) (7 / .75) + 1); static { ARRAY_CLASS_TO_SUPPORT.put(long[].class, LONG_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(int[].class, INT_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(short[].class, SHORT_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(double[].class, DOUBLE_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(float[].class, FLOAT_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(boolean[].class, BOOLEAN_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(String[].class, STRING_ARRAY); } public static boolean isSupportedPrimitiveArray(Object obj) { return obj != null && ARRAY_CLASS_TO_SUPPORT.containsKey(obj.getClass()); } public static PrimitiveArraySupport getArraySupport(A array) { return ARRAY_CLASS_TO_SUPPORT.get(array.getClass()); } }