@Override public final String formatJSON() { List<Map<String, String>> f = new ArrayList<Map<String, String>>(); List<List<Object>> r = new ArrayList<List<Object>>(); Map<String, String> fieldMap; for (Field<?> field : fields) { fieldMap = new LinkedHashMap<String, String>(); fieldMap.put("name", field.getName()); fieldMap.put("type", field.getDataType().getTypeName().toUpperCase()); f.add(fieldMap); } for (Record record : this) { List<Object> list = new ArrayList<Object>(); for (int index = 0; index < fields.fields.length; index++) { list.add(record.getValue(index)); } r.add(list); } Map<String, List<?>> map = new LinkedHashMap<String, List<?>>(); map.put("fields", f); map.put("records", r); return JSONObject.toJSONString(map); }
@Override public final String formatHTML() { StringBuilder sb = new StringBuilder(); sb.append("<table>"); sb.append("<thead>"); sb.append("<tr>"); for (Field<?> field : fields) { sb.append("<th>"); sb.append(field.getName()); sb.append("</th>"); } sb.append("</tr>"); sb.append("</thead>"); sb.append("<tbody>"); for (Record record : this) { sb.append("<tr>"); for (int index = 0; index < fields.fields.length; index++) { sb.append("<td>"); sb.append(format0(record.getValue(index), false)); sb.append("</td>"); } sb.append("</tr>"); } sb.append("</tbody>"); sb.append("</table>"); return sb.toString(); }
@Override public final String formatCSV(char delimiter, String nullString) { StringBuilder sb = new StringBuilder(); String sep1 = ""; for (Field<?> field : fields) { sb.append(sep1); sb.append(formatCSV0(field.getName(), "")); sep1 = Character.toString(delimiter); } sb.append("\n"); for (Record record : this) { String sep2 = ""; for (int index = 0; index < fields.fields.length; index++) { sb.append(sep2); sb.append(formatCSV0(record.getValue(index), nullString)); sep2 = Character.toString(delimiter); } sb.append("\n"); } return sb.toString(); }
/** * Get the returning record in those dialects that do not support fetching arbitrary fields from * JDBC's {@link Statement#getGeneratedKeys()} method. */ @SuppressWarnings("unchecked") private final void selectReturning(Configuration configuration, Object... values) { if (values != null && values.length > 0) { // This shouldn't be null, as relevant dialects should // return empty generated keys ResultSet if (into.getIdentity() != null) { Field<Number> field = (Field<Number>) into.getIdentity().getField(); Number[] ids = new Number[values.length]; for (int i = 0; i < values.length; i++) { ids[i] = field.getDataType().convert(values[i]); } // Only the IDENTITY value was requested. No need for an // additional query if (returning.size() == 1 && new Fields(returning).field(field) != null) { for (Number id : ids) { R typed = Utils.newRecord(into, configuration); ((AbstractRecord) typed).setValue(field, new Value<Number>(id)); getReturnedRecords().add(typed); } } // Other values are requested, too. Run another query else { returned = create(configuration) .select(returning) .from(into) .where(field.in(ids)) .fetchInto(into); } } } }
private final QueryPartInternal simulateSymmetric() { if (not) { return (QueryPartInternal) field.notBetween(minValue, maxValue).and(field.notBetween(maxValue, minValue)); } else { return (QueryPartInternal) field.between(minValue, maxValue).or(field.between(maxValue, minValue)); } }
@Override public final boolean isSigned(int column) throws SQLException { checkNotClosed(); Field<?> field = result.getField(column - 1); Class<?> type = field.getType(); return Number.class.isAssignableFrom(type) && !UNumber.class.isAssignableFrom(type); }
@Override public final void from(Object source, Field<?>... f) { if (source == null) return; // [#1987] Distinguish between various types to load data from // Maps are loaded using a {field-name -> value} convention if (source instanceof Map) { fromMap((Map<String, ?>) source, f); } // Arrays are loaded through index mapping else if (source instanceof Object[]) { fromArray((Object[]) source, f); } // All other types are expected to be POJOs else { Class<?> type = source.getClass(); try { boolean useAnnotations = hasColumnAnnotations(configuration(), type); for (Field<?> field : f) { List<java.lang.reflect.Field> members; Method method; // Annotations are available and present if (useAnnotations) { members = getAnnotatedMembers(configuration(), type, field.getName()); method = getAnnotatedGetter(configuration(), type, field.getName()); } // No annotations are present else { members = getMatchingMembers(configuration(), type, field.getName()); method = getMatchingGetter(configuration(), type, field.getName()); } // Use only the first applicable method or member if (method != null) { Utils.setValue(this, field, method.invoke(source)); } else if (members.size() > 0) { from(source, members.get(0), field); } } } // All reflection exceptions are intercepted catch (Exception e) { throw new MappingException("An error ocurred when mapping record from " + type, e); } } }
static <T, U> U getFromResultSet(ExecuteContext ctx, Field<U> field, int index) throws SQLException { @SuppressWarnings("unchecked") Converter<T, U> converter = (Converter<T, U>) DataTypes.converter(field.getType()); if (converter != null) { return converter.from(getFromResultSet(ctx, converter.fromType(), index)); } else { return getFromResultSet(ctx, field.getType(), index); } }
@Override public final Document intoXML() { final int size = getFields().size(); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.newDocument(); Element eResult = document.createElement("result"); eResult.setAttribute("xmlns", "http://www.jooq.org/xsd/jooq-export-2.6.0.xsd"); document.appendChild(eResult); Element eFields = document.createElement("fields"); eResult.appendChild(eFields); for (Field<?> field : getFields()) { Element eField = document.createElement("field"); eField.setAttribute("name", field.getName()); eField.setAttribute("type", field.getDataType().getTypeName().toUpperCase()); eFields.appendChild(eField); } Element eRecords = document.createElement("records"); eResult.appendChild(eRecords); for (Record record : this) { Element eRecord = document.createElement("record"); eRecords.appendChild(eRecord); for (int index = 0; index < size; index++) { Field<?> field = getField(index); Object value = record.getValue(index); Element eValue = document.createElement("value"); eValue.setAttribute("field", field.getName()); eRecord.appendChild(eValue); if (value != null) { eValue.setTextContent(format0(value)); } } } return document; } catch (ParserConfigurationException ignore) { throw new RuntimeException(ignore); } }
@Override public final String formatXML() { final int size = getFields().size(); StringBuilder sb = new StringBuilder(); sb.append("<result xmlns=\"http://www.jooq.org/xsd/jooq-export-2.6.0.xsd\">"); sb.append("<fields>"); for (Field<?> field : getFields()) { sb.append("<field name=\""); sb.append(escapeXML(field.getName())); sb.append("\" "); sb.append("type=\""); sb.append(field.getDataType().getTypeName().toUpperCase()); sb.append("\"/>"); } sb.append("</fields>"); sb.append("<records>"); for (Record record : this) { sb.append("<record>"); for (int index = 0; index < size; index++) { Field<?> field = getField(index); Object value = record.getValue(index); sb.append("<value field=\""); sb.append(escapeXML(field.getName())); sb.append("\""); if (value == null) { sb.append("/>"); } else { sb.append(">"); sb.append(escapeXML(format0(value))); sb.append("</value>"); } } sb.append("</record>"); } sb.append("</records>"); sb.append("</result>"); return sb.toString(); }
private String[] getTableColumnNames() { if (databaseDescriptor == null) { return new String[0]; } Set<String> columnNameSet = new HashSet<String>(); for (Table<?> table : databaseDescriptor.getSchema().getTables()) { for (Field<?> field : table.getFields()) { String columnName = field.getName(); columnNameSet.add(columnName); } } String[] columnNames = columnNameSet.toArray(new String[0]); Arrays.sort(columnNames, String.CASE_INSENSITIVE_ORDER); return columnNames; }
@Override public final Map<String, Object> intoMap() { Map<String, Object> map = new LinkedHashMap<String, Object>(); int size = fields.size(); for (int i = 0; i < size; i++) { Field<?> field = fields.field(i); if (map.put(field.getName(), getValue(i)) != null) { throw new InvalidResultException( "Field " + field.getName() + " is not unique in Record : " + this); } } return map; }
private final QueryPart delegate(Configuration configuration) { switch (configuration.family()) { case CUBRID: // There is a bug in CUBRID preventing reuse of "level" in the // predicate http://jira.cubrid.org/browse/ENGINE-119 Field<Integer> level = from.add(level()).sub(one()); return table( "({select} {0} {as} {1} {from} {2} {connect by} {level} <= {3})", level, name("generate_series"), new Dual(), to.add(one()).sub(from)); case POSTGRES: default: return table("{generate_series}({0}, {1})", from, to); } }
// Generic type safety... private final <Z extends UDTRecord<?>> void addAddressValue(StoreQuery<?> q, Field<Z> field) throws Exception { Class<? extends Z> addressType = field.getType(); Class<?> countryType = addressType.getMethod("getCountry").getReturnType(); Class<?> streetType = addressType.getMethod("getStreet").getReturnType(); Object country = null; try { countryType.getMethod("valueOf", String.class).invoke(countryType, "Germany"); } catch (NoSuchMethodException e) { country = "Germany"; } Object street = streetType.newInstance(); Z address = addressType.newInstance(); streetType.getMethod("setStreet", String.class).invoke(street, "Bahnhofstrasse"); streetType.getMethod("setNo", String.class).invoke(street, "1"); addressType.getMethod("setCountry", countryType).invoke(address, country); addressType.getMethod("setCity", String.class).invoke(address, "Calw"); addressType.getMethod("setStreet", streetType).invoke(address, street); q.addValue(field, address); }
@Override public R operate(R target) throws MappingException { AbstractRecord source = AbstractRecord.this; try { // [#1522] [#2989] If possible the complete state of this record should be copied onto the // other record if (target instanceof AbstractRecord) { AbstractRecord t = (AbstractRecord) target; // Iterate over target fields, to avoid ambiguities when two source fields share the same // name. for (int targetIndex = 0; targetIndex < t.size(); targetIndex++) { Field<?> targetField = t.field(targetIndex); int sourceIndex = fields.indexOf(targetField); if (sourceIndex >= 0) { DataType<?> targetType = targetField.getDataType(); Value<?> sourceValue = values[sourceIndex]; t.values[targetIndex] = new Value<Object>( targetType.convert(sourceValue.getValue()), targetType.convert(sourceValue.getOriginal()), sourceValue.isChanged()); } } } else { for (Field<?> targetField : target.fields()) { Field<?> sourceField = field(targetField); if (sourceField != null) { Utils.setValue(target, targetField, source, sourceField); } } } return target; } // All reflection exceptions are intercepted catch (Exception e) { throw new MappingException("An error ocurred when mapping record to " + target, e); } }
Expression(ExpressionOperator operator, Field<T> lhs, Field<?>... rhs) { super(operator.toSQL(), lhs.getDataType(), Util.combine(lhs, rhs)); this.operator = operator; this.lhs = lhs; this.rhs = new FieldList(); this.rhs.addAll(Arrays.asList(rhs)); }
@Override final Field<BigDecimal> getFunction0(Configuration configuration) { switch (configuration.dialect().family()) { case ASE: case CUBRID: case HSQLDB: case INGRES: case MARIADB: case MYSQL: case POSTGRES: case SQLSERVER: case SYBASE: return DSL.exp(argument.mul(two())).sub(one()).div(DSL.exp(argument.mul(two())).add(one())); default: return function("tanh", SQLDataType.NUMERIC, argument); } }
@SuppressWarnings("unchecked") @Override final Field<T> getFunction0(Configuration configuration) { // In any dialect, a single argument is always the greatest if (getArguments().length == 1) { return (Field<T>) getArguments()[0]; } switch (configuration.dialect()) { // This implementation has O(2^n) complexity. Better implementations // are very welcome // [#1049] TODO Fix this! case ASE: case DERBY: case SQLSERVER: case SYBASE: { Field<T> first = (Field<T>) getArguments()[0]; Field<T> other = (Field<T>) getArguments()[1]; if (getArguments().length > 2) { Field<?>[] remaining = new Field[getArguments().length - 2]; System.arraycopy(getArguments(), 2, remaining, 0, remaining.length); return DSL.decode() .when(first.greaterThan(other), DSL.greatest(first, remaining)) .otherwise(DSL.greatest(other, remaining)); } else { return DSL.decode().when(first.greaterThan(other), first).otherwise(other); } } case FIREBIRD: return function("maxvalue", getDataType(), getArguments()); case SQLITE: return function("max", getDataType(), getArguments()); default: return function("greatest", getDataType(), getArguments()); } }
@SuppressWarnings("unchecked") private final Merge<R> toMerge(Configuration configuration) { Table<R> i = getInto(); if (i.getPrimaryKey() != null) { Condition condition = null; List<Field<?>> key = new ArrayList<Field<?>>(); for (Field<?> f : i.getPrimaryKey().getFields()) { Field<Object> field = (Field<Object>) f; Field<Object> value = (Field<Object>) insertMaps.getMap().get(field); key.add(value); Condition other = field.equal(value); if (condition == null) { condition = other; } else { condition = condition.and(other); } } MergeOnConditionStep<R> on = create(configuration).mergeInto(i).usingDual().on(condition); // [#1295] Use UPDATE clause only when with ON DUPLICATE KEY UPDATE, // not with ON DUPLICATE KEY IGNORE MergeNotMatchedStep<R> notMatched = on; if (onDuplicateKeyUpdate) { notMatched = on.whenMatchedThenUpdate().set(updateMap); } return notMatched .whenNotMatchedThenInsert(insertMaps.getMap().keySet()) .values(insertMaps.getMap().values()); } else { throw new IllegalStateException( "The ON DUPLICATE KEY IGNORE/UPDATE clause cannot be simulated when inserting into non-updatable tables : " + getInto()); } }
@SuppressWarnings("unchecked") @Override final Field<T> getFunction0(Configuration configuration) { // In any dialect, a single argument is always the least if (getArguments().length == 1) { return (Field<T>) getArguments()[0]; } switch (configuration.family()) { // This implementation has O(2^n) complexity. Better implementations // are very welcome case DERBY: { Field<T> first = (Field<T>) getArguments()[0]; Field<T> other = (Field<T>) getArguments()[1]; if (getArguments().length > 2) { Field<?>[] remaining = new Field<?>[getArguments().length - 2]; System.arraycopy(getArguments(), 2, remaining, 0, remaining.length); return DSL.when(first.lessThan(other), DSL.least(first, remaining)) .otherwise(DSL.least(other, remaining)); } else { return DSL.when(first.lessThan(other), first).otherwise(other); } } case FIREBIRD: return function("minvalue", getDataType(), getArguments()); case SQLITE: return function("min", getDataType(), getArguments()); default: return function("least", getDataType(), getArguments()); } }
private static final Fields init(String alias, Class<?> arrayType) { List<Field<?>> result = new ArrayList<Field<?>>(); // [#1114] VARRAY/TABLE of OBJECT have more than one field if (UDTRecord.class.isAssignableFrom(arrayType)) { try { UDTRecord<?> record = (UDTRecord<?>) arrayType.newInstance(); for (Field<?> f : record.fields()) { result.add(fieldByName(f.getDataType(), alias, f.getName())); } } catch (Exception e) { throw new DataTypeException("Bad UDT Type : " + arrayType, e); } } // Simple array types have a synthetic field called "COLUMN_VALUE" else { result.add(fieldByName(Factory.getDataType(arrayType), alias, "COLUMN_VALUE")); } return new Fields(result); }
ImmutablePOJOMapperWithConstructorProperties( Constructor<E> constructor, ConstructorProperties properties) { this.constructor = constructor; this.propertyNames = Arrays.asList(properties.value()); this.useAnnotations = hasColumnAnnotations(configuration, type); this.parameterTypes = constructor.getParameterTypes(); this.parameterValues = new Object[parameterTypes.length]; this.members = new List[fields.length]; this.methods = new Method[fields.length]; this.propertyIndexes = new Integer[fields.length]; for (int i = 0; i < fields.length; i++) { Field<?> field = fields[i]; String name = field.getName(); String nameLC = StringUtils.toCamelCaseLC(name); // Annotations are available and present if (useAnnotations) { members[i] = getAnnotatedMembers(configuration, type, name); methods[i] = getAnnotatedGetter(configuration, type, name); } // No annotations are present else { members[i] = getMatchingMembers(configuration, type, name); methods[i] = getMatchingGetter(configuration, type, name); } // [#3911] Liberal interpretation of the @ConstructorProperties specs: // We also accept properties that don't have a matching getter or member for (int j = 0; j < propertyNames.size(); j++) { if (name.equals(propertyNames.get(j)) || nameLC.equals(propertyNames.get(j))) { propertyIndexes[i] = j; break; } } } }
@Override public final String formatHTML() { final int size = getFields().size(); StringBuilder sb = new StringBuilder(); sb.append("<table>"); sb.append("<thead>"); sb.append("<tr>"); for (Field<?> field : getFields()) { sb.append("<th>"); sb.append(field.getName()); sb.append("</th>"); } sb.append("</tr>"); sb.append("</thead>"); sb.append("<tbody>"); for (Record record : this) { sb.append("<tr>"); for (int index = 0; index < size; index++) { sb.append("<td>"); sb.append(format0(record.getValue(index))); sb.append("</td>"); } sb.append("</tr>"); } sb.append("</tbody>"); sb.append("</table>"); return sb.toString(); }
@SuppressWarnings({"unchecked"}) ArrayTable(Field<?> array, String alias) { super(alias); Class<?> arrayType; // TODO [#523] Solve this in a more object-oriented way... if (array.getDataType().getType().isArray()) { arrayType = array.getDataType().getType().getComponentType(); } // [#1110] Keep track of element type information of Oracle VARRAY / TABLE types else if (array instanceof ArrayConstant) { arrayType = array.getDataType().getType(); } // [#1111] Keep track of element type information of Oracle // VARRAY / TABLE types returned from functions else if (ArrayRecord.class.isAssignableFrom(array.getDataType().getType())) { // TODO [#523] This information should be available in ARRAY meta-data ArrayRecord<?> dummy = Utils.newArrayRecord( (Class<ArrayRecord<?>>) array.getDataType().getType(), new DefaultConfiguration()); arrayType = dummy.getDataType().getType(); } // Is this case possible? else { arrayType = Object.class; } this.array = array; this.alias = alias; this.field = init(alias, arrayType); init(alias, arrayType); }
private final Table<Record> table(Configuration configuration) { switch (configuration.getDialect()) { case ORACLE: { if (array.getDataType().getType().isArray()) { return simulate().as(alias); } else { return new OracleArrayTable().as(alias); } } case H2: { return new H2ArrayTable().as(alias); } // [#756] These dialects need special care when aliasing unnested // arrays case HSQLDB: case POSTGRES: { return new PostgresHSQLDBTable().as(alias); } // Other dialects can simulate unnested arrays using UNION ALL default: { if (array.getDataType().getType().isArray() && array instanceof Param) { return simulate(); } else { throw new SQLDialectNotSupportedException( "ARRAY TABLE is not supported for " + configuration.getDialect()); } } } }
@Override final Field<T> getFunction0(Configuration configuration) { switch (configuration.family()) { case H2: case HSQLDB: return field("{nvl}({0}, {1})", getDataType(), arg1, arg2); case DERBY: case POSTGRES: return field("{coalesce}({0}, {1})", getDataType(), arg1, arg2); case MARIADB: case MYSQL: case SQLITE: return field("{ifnull}({0}, {1})", getDataType(), arg1, arg2); default: return DSL.when(arg1.isNotNull(), arg1).otherwise(arg2); } }
@Override protected final void prepare(ExecuteContext ctx) throws SQLException { Connection connection = ctx.connection(); // Just in case, always set Sybase ASE statement mode to return // Generated keys if client code wants to SELECT @@identity afterwards if (ctx.configuration().dialect() == SQLDialect.ASE) { ctx.statement(connection.prepareStatement(ctx.sql(), Statement.RETURN_GENERATED_KEYS)); return; } // Normal statement preparing if no values should be returned else if (returning.isEmpty()) { super.prepare(ctx); return; } // Values should be returned from the INSERT else { switch (ctx.configuration().dialect()) { // Postgres uses the RETURNING clause in SQL case FIREBIRD: case POSTGRES: // SQLite will select last_insert_rowid() after the INSER case SQLITE: // Sybase will select @@identity after the INSERT case CUBRID: case SYBASE: super.prepare(ctx); return; // Some dialects can only return AUTO_INCREMENT values // Other values have to be fetched in a second step // [#1260] TODO CUBRID supports this, but there's a JDBC bug case ASE: case DERBY: case H2: case INGRES: case MYSQL: case SQLSERVER: ctx.statement(connection.prepareStatement(ctx.sql(), Statement.RETURN_GENERATED_KEYS)); return; // The default is to return all requested fields directly case DB2: case HSQLDB: case ORACLE: default: { List<String> names = new ArrayList<String>(); for (Field<?> field : returning) { names.add(field.getName()); } ctx.statement( connection.prepareStatement(ctx.sql(), names.toArray(new String[names.size()]))); return; } } } }
public static <T> void writeToSQLOutput(SQLOutput stream, Field<T> field, T value) throws SQLException { Class<? extends T> type = field.getType(); writeToSQLOutput(stream, type, field.getDataType(), value); }
@SuppressWarnings("unchecked") public static <T> T getFromSQLInput(Configuration configuration, SQLInput stream, Field<T> field) throws SQLException { Class<? extends T> type = field.getType(); DataType<T> dataType = field.getDataType(); if (type == Blob.class) { return (T) stream.readBlob(); } else if (type == Boolean.class) { return (T) checkWasNull(stream, Boolean.valueOf(stream.readBoolean())); } else if (type == BigInteger.class) { BigDecimal result = stream.readBigDecimal(); return (T) (result == null ? null : result.toBigInteger()); } else if (type == BigDecimal.class) { return (T) stream.readBigDecimal(); } else if (type == Byte.class) { return (T) checkWasNull(stream, Byte.valueOf(stream.readByte())); } else if (type == byte[].class) { // [#1327] Oracle cannot deserialise BLOBs as byte[] from SQLInput if (dataType.isLob()) { Blob blob = null; try { blob = stream.readBlob(); return (T) (blob == null ? null : blob.getBytes(1, (int) blob.length())); } finally { Util.safeFree(blob); } } else { return (T) stream.readBytes(); } } else if (type == Clob.class) { return (T) stream.readClob(); } else if (type == Date.class) { return (T) stream.readDate(); } else if (type == Double.class) { return (T) checkWasNull(stream, Double.valueOf(stream.readDouble())); } else if (type == Float.class) { return (T) checkWasNull(stream, Float.valueOf(stream.readFloat())); } else if (type == Integer.class) { return (T) checkWasNull(stream, Integer.valueOf(stream.readInt())); } else if (type == Long.class) { return (T) checkWasNull(stream, Long.valueOf(stream.readLong())); } else if (type == Short.class) { return (T) checkWasNull(stream, Short.valueOf(stream.readShort())); } else if (type == String.class) { return (T) stream.readString(); } else if (type == Time.class) { return (T) stream.readTime(); } else if (type == Timestamp.class) { return (T) stream.readTimestamp(); } else if (type == YearToMonth.class) { String string = stream.readString(); return (T) (string == null ? null : YearToMonth.valueOf(string)); } else if (type == DayToSecond.class) { String string = stream.readString(); return (T) (string == null ? null : DayToSecond.valueOf(string)); } else if (type == UByte.class) { String string = stream.readString(); return (T) (string == null ? null : UByte.valueOf(string)); } else if (type == UShort.class) { String string = stream.readString(); return (T) (string == null ? null : UShort.valueOf(string)); } else if (type == UInteger.class) { String string = stream.readString(); return (T) (string == null ? null : UInteger.valueOf(string)); } else if (type == ULong.class) { String string = stream.readString(); return (T) (string == null ? null : ULong.valueOf(string)); } // The type byte[] is handled earlier. byte[][] can be handled here else if (type.isArray()) { Array result = stream.readArray(); return (T) (result == null ? null : result.getArray()); } else if (ArrayRecord.class.isAssignableFrom(type)) { return (T) getArrayRecord(configuration, stream.readArray(), (Class<? extends ArrayRecord<?>>) type); } else if (EnumType.class.isAssignableFrom(type)) { return getEnumType(type, stream.readString()); } else if (MasterDataType.class.isAssignableFrom(type)) { return (T) getMasterDataType(type, stream.readObject()); } else if (UDTRecord.class.isAssignableFrom(type)) { return (T) stream.readObject(); } else { return (T) stream.readObject(); } }
private static <T> void pgSetValue(UDTRecord<?> record, Field<T> field, String value) throws SQLException { record.setValue(field, pgFromString(field.getType(), value)); }