Search in sources :

Example 1 with RenderContext

use of org.jooq.RenderContext in project jOOQ by jOOQ.

the class Tools method renderAndBind.

/**
     * Render and bind a list of {@link QueryPart} to plain SQL
     * <p>
     * This will perform two actions:
     * <ul>
     * <li>When {@link RenderContext} is provided, it will render plain SQL to
     * the context, substituting {numbered placeholders} and bind values if
     * {@link RenderContext#inline()} is set</li>
     * <li>When {@link BindContext} is provided, it will bind the list of
     * {@link QueryPart} according to the {numbered placeholders} and bind
     * values in the sql string</li>
     * </ul>
     */
@SuppressWarnings("null")
static final void renderAndBind(Context<?> ctx, String sql, List<QueryPart> substitutes) {
    RenderContext render = (RenderContext) ((ctx instanceof RenderContext) ? ctx : null);
    BindContext bind = (BindContext) ((ctx instanceof BindContext) ? ctx : null);
    int substituteIndex = 0;
    char[] sqlChars = sql.toCharArray();
    // [#1593] Create a dummy renderer if we're in bind mode
    if (render == null)
        render = new DefaultRenderContext(bind.configuration());
    SQLDialect dialect = render.configuration().dialect();
    SQLDialect family = dialect.family();
    boolean mysql = asList(MARIADB, MYSQL).contains(family);
    String[][] quotes = QUOTES.get(family);
    // [#3630] Depending on this setting, we need to consider backslashes as escape characters within string literals.
    boolean needsBackslashEscaping = needsBackslashEscaping(ctx.configuration());
    characterLoop: for (int i = 0; i < sqlChars.length; i++) {
        // where id = ?
        if (peek(sqlChars, i, "--") || // http://bugs.mysql.com/bug.php?id=76623
        (mysql && peek(sqlChars, i, "#"))) {
            // Consume the complete comment
            for (; i < sqlChars.length && sqlChars[i] != '\r' && sqlChars[i] != '\n'; render.sql(sqlChars[i++])) ;
            // Consume the newline character
            if (i < sqlChars.length)
                render.sql(sqlChars[i]);
        } else // from t_book where id = ?
        if (peek(sqlChars, i, "/*")) {
            // Consume the complete comment
            for (; !peek(sqlChars, i, "*/"); render.sql(sqlChars[i++])) ;
            // Consume the comment delimiter
            render.sql(sqlChars[i++]);
            render.sql(sqlChars[i]);
        } else // insert into x values ('Hello? Anybody out there?');
        if (sqlChars[i] == '\'') {
            // Consume the initial string literal delimiter
            render.sql(sqlChars[i++]);
            // Consume the whole string literal
            for (; ; ) {
                // [#3000] [#3630] Consume backslash-escaped characters if needed
                if (sqlChars[i] == '\\' && needsBackslashEscaping) {
                    render.sql(sqlChars[i++]);
                } else // Consume an escaped apostrophe
                if (peek(sqlChars, i, "''")) {
                    render.sql(sqlChars[i++]);
                } else // Break on the terminal string literal delimiter
                if (peek(sqlChars, i, "'")) {
                    break;
                }
                // Consume string literal content
                render.sql(sqlChars[i++]);
            }
            // Consume the terminal string literal delimiter
            render.sql(sqlChars[i]);
        } else // update x set v = "Column Name with a ? (question mark)"
        if (peekAny(sqlChars, i, quotes[QUOTE_START_DELIMITER])) {
            // Main identifier delimiter or alternative one?
            int delimiter = 0;
            for (int d = 0; d < quotes[QUOTE_START_DELIMITER].length; d++) {
                if (peek(sqlChars, i, quotes[QUOTE_START_DELIMITER][d])) {
                    delimiter = d;
                    break;
                }
            }
            // Consume the initial identifier delimiter
            for (int d = 0; d < quotes[QUOTE_START_DELIMITER][delimiter].length(); d++) render.sql(sqlChars[i++]);
            // Consume the whole identifier
            for (; ; ) {
                // Consume an escaped quote
                if (peek(sqlChars, i, quotes[QUOTE_END_DELIMITER_ESCAPED][delimiter])) {
                    for (int d = 0; d < quotes[QUOTE_END_DELIMITER_ESCAPED][delimiter].length(); d++) render.sql(sqlChars[i++]);
                } else // Break on the terminal identifier delimiter
                if (peek(sqlChars, i, quotes[QUOTE_END_DELIMITER][delimiter])) {
                    break;
                }
                // Consume identifier content
                render.sql(sqlChars[i++]);
            }
            // Consume the terminal identifier delimiter
            for (int d = 0; d < quotes[QUOTE_END_DELIMITER][delimiter].length(); d++) {
                if (d > 0)
                    i++;
                render.sql(sqlChars[i]);
            }
        } else // Inline bind variables only outside of string literals
        if (substituteIndex < substitutes.size() && ((sqlChars[i] == '?') || //         Watch out for the PostgreSQL cast operator ::
        (sqlChars[i] == ':' && i + 1 < sqlChars.length && isJavaIdentifierPart(sqlChars[i + 1]) && (i - 1 < 0 || sqlChars[i - 1] != ':')))) {
            // [#5307] Consume PostgreSQL style operators. These aren't bind variables!
            if (sqlChars[i] == '?' && i + 1 < sqlChars.length) {
                for (String suffix : NON_BIND_VARIABLE_SUFFIXES) {
                    if (peek(sqlChars, i + 1, suffix)) {
                        for (int j = i; i - j <= suffix.length(); i++) render.sql(sqlChars[i]);
                        render.sql(sqlChars[i]);
                        continue characterLoop;
                    }
                }
            }
            // [#4131] Consume the named bind variable
            if (sqlChars[i] == ':')
                while (++i < sqlChars.length && isJavaIdentifierPart(sqlChars[i])) ;
            QueryPart substitute = substitutes.get(substituteIndex++);
            if (render.paramType() == INLINED || render.paramType() == NAMED || render.paramType() == NAMED_OR_INLINED) {
                render.visit(substitute);
            } else {
                CastMode previous = render.castMode();
                render.castMode(CastMode.NEVER).visit(substitute).castMode(previous);
            }
            if (bind != null) {
                bind.visit(substitute);
            }
        } else // [#1432] Inline substitues for {numbered placeholders} outside of string literals
        if (sqlChars[i] == '{') {
            // [#1461] Be careful not to match any JDBC escape syntax
            if (peekAny(sqlChars, i, JDBC_ESCAPE_PREFIXES, true)) {
                render.sql(sqlChars[i]);
            } else // Consume the whole token
            {
                int start = ++i;
                for (; i < sqlChars.length && sqlChars[i] != '}'; i++) ;
                int end = i;
                String token = sql.substring(start, end);
                // Try getting the {numbered placeholder}
                try {
                    QueryPart substitute = substitutes.get(Integer.valueOf(token));
                    render.visit(substitute);
                    if (bind != null) {
                        bind.visit(substitute);
                    }
                }// If the above failed, then we're dealing with a {keyword}
                 catch (NumberFormatException e) {
                    render.keyword(token);
                }
            }
        } else // Any other character
        {
            render.sql(sqlChars[i]);
        }
    }
}
Also used : BindContext(org.jooq.BindContext) RenderContext(org.jooq.RenderContext) SQLDialect(org.jooq.SQLDialect) QueryPart(org.jooq.QueryPart) CastMode(org.jooq.RenderContext.CastMode)

Example 2 with RenderContext

use of org.jooq.RenderContext in project jOOQ by jOOQ.

the class Val method accept.

// ------------------------------------------------------------------------
// XXX: Field API
// ------------------------------------------------------------------------
@Override
public void accept(Context<?> ctx) {
    if (ctx instanceof RenderContext) {
        ParamType paramType = ctx.paramType();
        if (isInline(ctx))
            ctx.paramType(ParamType.INLINED);
        try {
            getBinding().sql(new DefaultBindingSQLContext<T>(ctx.configuration(), ctx.data(), (RenderContext) ctx, value, getBindVariable(ctx)));
        } catch (SQLException e) {
            throw new DataAccessException("Error while generating SQL for Binding", e);
        }
        ctx.paramType(paramType);
    } else {
        // [#1302] Bind value only if it was not explicitly forced to be inlined
        if (!isInline(ctx)) {
            ctx.bindValue(value, this);
        }
    }
}
Also used : RenderContext(org.jooq.RenderContext) SQLException(java.sql.SQLException) ParamType(org.jooq.conf.ParamType) DataAccessException(org.jooq.exception.DataAccessException)

Example 3 with RenderContext

use of org.jooq.RenderContext in project jOOQ by jOOQ.

the class DefaultBinding method toSQL.

/**
     * Inlining abstraction
     */
@SuppressWarnings({ "unchecked", "rawtypes" })
private final void toSQL(BindingSQLContext<U> ctx, Object val) {
    SQLDialect family = ctx.family();
    RenderContext render = ctx.render();
    if (render.paramType() == INLINED) {
        if (val == null) {
            render.keyword("null");
        } else if (type == Boolean.class) {
            // [#1153] Some dialects don't support boolean literals TRUE and FALSE
            if (asList(FIREBIRD, SQLITE).contains(family)) {
                render.sql(((Boolean) val) ? "1" : "0");
            } else {
                render.keyword(((Boolean) val).toString());
            }
        } else // [#1154] Binary data cannot always be inlined
        if (type == byte[].class) {
            byte[] binary = (byte[]) val;
            if (asList().contains(family)) {
                render.sql("0x").sql(convertBytesToHex(binary));
            } else if (asList(DERBY, H2, HSQLDB, MARIADB, MYSQL, SQLITE).contains(family)) {
                render.sql("X'").sql(convertBytesToHex(binary)).sql('\'');
            } else if (asList().contains(family)) {
                render.keyword("hextoraw('").sql(convertBytesToHex(binary)).sql("')");
            } else if (family == POSTGRES) {
                render.sql("E'").sql(PostgresUtils.toPGString(binary)).keyword("'::bytea");
            } else // This default behaviour is used in debug logging for dialects
            // that do not support inlining binary data
            {
                render.sql("X'").sql(convertBytesToHex(binary)).sql('\'');
            }
        } else // Interval extends Number, so let Interval come first!
        if (Interval.class.isAssignableFrom(type)) {
            render.sql('\'').sql(escape(val, render)).sql('\'');
        } else // [#5249] Special inlining of special floating point values
        if (Double.class.isAssignableFrom(type) && ((Double) val).isNaN()) {
            if (POSTGRES == family)
                render.visit(inline("NaN")).sql("::").keyword("float8");
            else
                render.sql(((Number) val).toString());
        } else // [#5249] Special inlining of special floating point values
        if (Float.class.isAssignableFrom(type) && ((Float) val).isNaN()) {
            if (POSTGRES == family)
                render.visit(inline("NaN")).sql("::").keyword("float4");
            else
                render.sql(((Number) val).toString());
        } else if (Number.class.isAssignableFrom(type)) {
            render.sql(((Number) val).toString());
        } else // [#1156] DATE / TIME inlining is very vendor-specific
        if (Tools.isDate(type)) {
            Date date = getDate(type, val);
            // [#1253] SQL Server and Sybase do not implement date literals
            if (asList(SQLITE).contains(family)) {
                render.sql('\'').sql(escape(date, render)).sql('\'');
            } else // [#1253] Derby doesn't support the standard literal
            if (family == DERBY) {
                render.keyword("date('").sql(escape(date, render)).sql("')");
            } else // [#3648] Circumvent a MySQL bug related to date literals
            if (family == MYSQL) {
                render.keyword("{d '").sql(escape(date, render)).sql("'}");
            } else // Most dialects implement SQL standard date literals
            {
                render.keyword("date '").sql(escape(date, render)).sql('\'');
            }
        } else if (Tools.isTimestamp(type)) {
            Timestamp ts = getTimestamp(type, val);
            // [#1253] SQL Server and Sybase do not implement timestamp literals
            if (asList(SQLITE).contains(family)) {
                render.sql('\'').sql(escape(ts, render)).sql('\'');
            } else // [#1253] Derby doesn't support the standard literal
            if (family == DERBY) {
                render.keyword("timestamp('").sql(escape(ts, render)).sql("')");
            } else // CUBRID timestamps have no fractional seconds
            if (family == CUBRID) {
                render.keyword("datetime '").sql(escape(ts, render)).sql('\'');
            } else // [#3648] Circumvent a MySQL bug related to date literals
            if (family == MYSQL) {
                render.keyword("{ts '").sql(escape(ts, render)).sql("'}");
            } else // Most dialects implement SQL standard timestamp literals
            {
                render.keyword("timestamp '").sql(escape(ts, render)).sql('\'');
            }
        } else if (Tools.isTime(type)) {
            Time time = getTime(type, val);
            // [#1253] SQL Server and Sybase do not implement time literals
            if (asList(SQLITE).contains(family)) {
                render.sql('\'').sql(new SimpleDateFormat("HH:mm:ss").format(time)).sql('\'');
            } else // [#1253] Derby doesn't support the standard literal
            if (family == DERBY) {
                render.keyword("time").sql("('").sql(escape(time, render)).sql("')");
            } else // [#3648] Circumvent a MySQL bug related to date literals
            if (family == MYSQL) {
                render.keyword("{t '").sql(escape(time, render)).sql("'}");
            } else // Most dialects implement SQL standard time literals
            {
                render.keyword("time").sql(" '").sql(escape(time, render)).sql('\'');
            }
        } else if (type == OffsetDateTime.class) {
            String string = format((OffsetDateTime) val);
            // Some dialects implement SQL standard time literals
            {
                render.keyword("timestamp with time zone").sql(" '").sql(escape(string, render)).sql('\'');
            }
        } else if (type == OffsetTime.class) {
            String string = format((OffsetTime) val);
            // Some dialects implement SQL standard time literals
            {
                render.keyword("time with time zone").sql(" '").sql(escape(string, render)).sql('\'');
            }
        } else if (type.isArray()) {
            String separator = "";
            // H2 renders arrays as rows
            if (family == H2) {
                render.sql('(');
                for (Object o : ((Object[]) val)) {
                    render.sql(separator);
                    new DefaultBinding<Object, Object>(Converters.identity((Class) type.getComponentType()), isLob).sql(new DefaultBindingSQLContext<Object>(ctx.configuration(), ctx.data(), ctx.render(), o));
                    separator = ", ";
                }
                render.sql(')');
            } else if (family == POSTGRES) {
                render.visit(cast(inline(PostgresUtils.toPGArrayString((Object[]) val)), type));
            } else // By default, render HSQLDB / POSTGRES syntax
            {
                render.keyword("ARRAY");
                render.sql('[');
                for (Object o : ((Object[]) val)) {
                    render.sql(separator);
                    new DefaultBinding<Object, Object>(Converters.identity((Class) type.getComponentType()), isLob).sql(new DefaultBindingSQLContext<Object>(ctx.configuration(), ctx.data(), ctx.render(), o));
                    separator = ", ";
                }
                render.sql(']');
                // [#3214] Some PostgreSQL array type literals need explicit casting
                if (family == POSTGRES && EnumType.class.isAssignableFrom(type.getComponentType())) {
                    pgRenderEnumCast(render, type);
                }
            }
        } else if (EnumType.class.isAssignableFrom(type)) {
            String literal = ((EnumType) val).getLiteral();
            if (literal == null) {
                new DefaultBinding<Object, Object>(Converters.identity((Class) String.class), isLob).sql(new DefaultBindingSQLContext<Object>(ctx.configuration(), ctx.data(), ctx.render(), literal));
            } else {
                new DefaultBinding<Object, Object>(Converters.identity((Class) String.class), isLob).sql(new DefaultBindingSQLContext<Object>(ctx.configuration(), ctx.data(), ctx.render(), literal));
            }
        } else if (UDTRecord.class.isAssignableFrom(type)) {
            render.sql("[UDT]");
        } else // Known fall-through types:
        // - Blob, Clob (both not supported by jOOQ)
        // - String
        // - UUID
        {
            render.sql('\'').sql(escape(val, render), true).sql('\'');
        }
    } else // In Postgres, some additional casting must be done in some cases...
    if (family == SQLDialect.POSTGRES) {
        // Postgres needs explicit casting for enum (array) types
        if (EnumType.class.isAssignableFrom(type) || (type.isArray() && EnumType.class.isAssignableFrom(type.getComponentType()))) {
            render.sql(ctx.variable());
            pgRenderEnumCast(render, type);
        } else // ... and also for other array types
        if (type.isArray() && byte[].class != type) {
            render.sql(ctx.variable());
            render.sql("::");
            render.keyword(DefaultDataType.getDataType(family, type).getCastTypeName(render.configuration()));
        } else {
            render.sql(ctx.variable());
        }
    } else {
        render.sql(ctx.variable());
    }
}
Also used : RenderContext(org.jooq.RenderContext) Time(java.sql.Time) LocalTime(java.time.LocalTime) OffsetTime(java.time.OffsetTime) OffsetDateTime(java.time.OffsetDateTime) LocalDateTime(java.time.LocalDateTime) PostgresUtils.toPGArrayString(org.jooq.util.postgres.PostgresUtils.toPGArrayString) Timestamp(java.sql.Timestamp) LocalDate(java.time.LocalDate) Date(java.sql.Date) UNumber(org.jooq.types.UNumber) SQLDialect(org.jooq.SQLDialect) OffsetTime(java.time.OffsetTime) EnumType(org.jooq.EnumType) SimpleDateFormat(java.text.SimpleDateFormat) PostgresUtils.toPGInterval(org.jooq.util.postgres.PostgresUtils.toPGInterval) Interval(org.jooq.types.Interval)

Example 4 with RenderContext

use of org.jooq.RenderContext in project jOOQ by jOOQ.

the class RowSubqueryCondition method delegate.

private final QueryPartInternal delegate(Context<?> ctx) {
    final Configuration configuration = ctx.configuration();
    final RenderContext render = ctx instanceof RenderContext ? (RenderContext) ctx : null;
    SQLDialect family = configuration.dialect().family();
    // [#3505] TODO: Emulate this where it is not supported
    if (rightQuantified != null) {
        return new Native();
    } else // predicates with row value expressions and subqueries:
    if (asList(H2, HSQLDB, MARIADB, MYSQL, POSTGRES).contains(family)) {
        return new Native();
    } else // [#2395] All other configurations have to be emulated
    {
        String table = render == null ? "t" : render.nextAlias();
        List<String> names = new ArrayList<String>();
        for (int i = 0; i < left.size(); i++) {
            names.add(table + "_" + i);
        }
        Field<?>[] fields = new Field[names.size()];
        for (int i = 0; i < fields.length; i++) {
            fields[i] = field(name(table, names.get(i)));
        }
        Condition condition;
        switch(comparator) {
            case GREATER:
                condition = ((RowN) left).gt(row(fields));
                break;
            case GREATER_OR_EQUAL:
                condition = ((RowN) left).ge(row(fields));
                break;
            case LESS:
                condition = ((RowN) left).lt(row(fields));
                break;
            case LESS_OR_EQUAL:
                condition = ((RowN) left).le(row(fields));
                break;
            case IN:
            case EQUALS:
            case NOT_IN:
            case NOT_EQUALS:
            default:
                condition = ((RowN) left).eq(row(fields));
                break;
        }
        Select<Record> subselect = select().from(right.asTable(table, names.toArray(EMPTY_STRING))).where(condition);
        switch(comparator) {
            case NOT_IN:
            case NOT_EQUALS:
                return (QueryPartInternal) notExists(subselect);
            default:
                return (QueryPartInternal) exists(subselect);
        }
    }
}
Also used : Condition(org.jooq.Condition) RenderContext(org.jooq.RenderContext) RowN(org.jooq.RowN) Configuration(org.jooq.Configuration) SQLDialect(org.jooq.SQLDialect) Select(org.jooq.Select) QuantifiedSelect(org.jooq.QuantifiedSelect) ArrayList(java.util.ArrayList) Arrays.asList(java.util.Arrays.asList) List(java.util.List)

Aggregations

RenderContext (org.jooq.RenderContext)4 SQLDialect (org.jooq.SQLDialect)3 Date (java.sql.Date)1 SQLException (java.sql.SQLException)1 Time (java.sql.Time)1 Timestamp (java.sql.Timestamp)1 SimpleDateFormat (java.text.SimpleDateFormat)1 LocalDate (java.time.LocalDate)1 LocalDateTime (java.time.LocalDateTime)1 LocalTime (java.time.LocalTime)1 OffsetDateTime (java.time.OffsetDateTime)1 OffsetTime (java.time.OffsetTime)1 ArrayList (java.util.ArrayList)1 Arrays.asList (java.util.Arrays.asList)1 List (java.util.List)1 BindContext (org.jooq.BindContext)1 Condition (org.jooq.Condition)1 Configuration (org.jooq.Configuration)1 EnumType (org.jooq.EnumType)1 QuantifiedSelect (org.jooq.QuantifiedSelect)1