From cf675a88772c8baa0fa4dbfe0cf50d0f6e5d91a6 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 30 Oct 2014 14:51:15 +0100 Subject: [PATCH] [#3248] Add support for Binding (i.e. "Type Providers") --- .../jOOQ-spring-boot-example/.gitignore | 1 + jOOQ/src/main/java/org/jooq/Binding.java | 184 ++ .../main/java/org/jooq/BindingContext.java | 49 + .../org/jooq/BindingGetResultSetContext.java | 67 + .../org/jooq/BindingGetSQLInputContext.java | 62 + .../org/jooq/BindingGetStatementContext.java | 67 + .../java/org/jooq/BindingRegisterContext.java | 63 + .../main/java/org/jooq/BindingSQLContext.java | 60 + .../org/jooq/BindingSetSQLOutputContext.java | 62 + .../org/jooq/BindingSetStatementContext.java | 67 + jOOQ/src/main/java/org/jooq/Context.java | 84 +- jOOQ/src/main/java/org/jooq/DSLContext.java | 32 +- .../main/java/org/jooq/ExecuteContext.java | 31 +- jOOQ/src/main/java/org/jooq/Field.java | 5 + jOOQ/src/main/java/org/jooq/Parameter.java | 5 + .../src/main/java/org/jooq/RecordContext.java | 33 +- jOOQ/src/main/java/org/jooq/Scope.java | 125 + .../java/org/jooq/TransactionContext.java | 32 +- jOOQ/src/main/java/org/jooq/VisitContext.java | 33 +- .../org/jooq/impl/AbstractBindingContext.java | 72 + .../java/org/jooq/impl/AbstractContext.java | 44 +- .../java/org/jooq/impl/AbstractField.java | 9 + .../java/org/jooq/impl/AbstractRoutine.java | 60 +- .../java/org/jooq/impl/AbstractScope.java | 109 + .../java/org/jooq/impl/ArrayRecordImpl.java | 2 +- .../main/java/org/jooq/impl/CursorImpl.java | 4 +- .../main/java/org/jooq/impl/DefaultArray.java | 115 + .../org/jooq/impl/DefaultBindContext.java | 344 +-- .../java/org/jooq/impl/DefaultBinding.java | 2080 +++++++++++++++++ .../org/jooq/impl/DefaultBindingContext.java | 53 + .../DefaultBindingGetResultSetContext.java | 82 + .../DefaultBindingGetSQLInputContext.java | 75 + .../DefaultBindingGetStatementContext.java | 82 + .../impl/DefaultBindingRegisterContext.java | 72 + .../jooq/impl/DefaultBindingSQLContext.java | 71 + .../DefaultBindingSetSQLOutputContext.java | 72 + .../DefaultBindingSetStatementContext.java | 79 + .../java/org/jooq/impl/DefaultDSLContext.java | 275 +-- .../org/jooq/impl/DefaultRenderContext.java | 2 +- .../jooq/impl/DefaultTransactionContext.java | 27 +- .../java/org/jooq/impl/ExistsOperator.java | 63 + .../java/org/jooq/impl/ParameterImpl.java | 9 + .../impl/SelectQueryAsExistsCondition.java | 102 + .../org/jooq/impl/SelectQueryAsField.java | 94 + .../impl/SelectQueryAsSubQueryCondition.java | 107 + .../org/jooq/impl/SelectQueryAsTable.java | 115 + .../java/org/jooq/impl/SelectQueryImpl.java | 2 +- .../java/org/jooq/impl/UDTRecordImpl.java | 6 +- jOOQ/src/main/java/org/jooq/impl/Utils.java | 1058 --------- jOOQ/src/main/java/org/jooq/impl/Val.java | 620 +---- 50 files changed, 4453 insertions(+), 2514 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/Binding.java create mode 100644 jOOQ/src/main/java/org/jooq/BindingContext.java create mode 100644 jOOQ/src/main/java/org/jooq/BindingGetResultSetContext.java create mode 100644 jOOQ/src/main/java/org/jooq/BindingGetSQLInputContext.java create mode 100644 jOOQ/src/main/java/org/jooq/BindingGetStatementContext.java create mode 100644 jOOQ/src/main/java/org/jooq/BindingRegisterContext.java create mode 100644 jOOQ/src/main/java/org/jooq/BindingSQLContext.java create mode 100644 jOOQ/src/main/java/org/jooq/BindingSetSQLOutputContext.java create mode 100644 jOOQ/src/main/java/org/jooq/BindingSetStatementContext.java create mode 100644 jOOQ/src/main/java/org/jooq/Scope.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/AbstractBindingContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/AbstractScope.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultArray.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultBindingContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetResultSetContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetSQLInputContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetStatementContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultBindingRegisterContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultBindingSQLContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetSQLOutputContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetStatementContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/ExistsOperator.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/SelectQueryAsExistsCondition.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/SelectQueryAsField.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/SelectQueryAsSubQueryCondition.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/SelectQueryAsTable.java diff --git a/jOOQ-examples/jOOQ-spring-boot-example/.gitignore b/jOOQ-examples/jOOQ-spring-boot-example/.gitignore index 26ae8c908e..4440e58198 100644 --- a/jOOQ-examples/jOOQ-spring-boot-example/.gitignore +++ b/jOOQ-examples/jOOQ-spring-boot-example/.gitignore @@ -3,3 +3,4 @@ .settings /target /*.iml +/bin/ diff --git a/jOOQ/src/main/java/org/jooq/Binding.java b/jOOQ/src/main/java/org/jooq/Binding.java new file mode 100644 index 0000000000..092d1efeeb --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Binding.java @@ -0,0 +1,184 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +import java.io.Serializable; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLInput; +import java.sql.SQLOutput; +import java.sql.Timestamp; + +import org.jooq.exception.DataAccessException; +import org.jooq.impl.DefaultBinding; + +/** + * An SPI (Service Provider Interface) that exposes all low-level interactions + * with JDBC bind variables. + *

+ * This SPI is used by jOOQ users to implement support for custom data types + * that would otherwise not be supported by jOOQ and/or JDBC. All of jOOQ's + * internal support for bind variable types is implemented in + * {@link DefaultBinding}. + * + * @author Lukas Eder + */ +public interface Binding extends Serializable { + + /** + * Generate SQL code for the bind variable. + *

+ * Implementations should generate SQL code onto + * {@link BindingSQLContext#render()}, given the context's bind variable + * located at {@link BindingSQLContext#value()}. Examples of such SQL code + * are: + *

+ *

+ * Implementations must provide consistent behaviour between + * {@link #sql(BindingSQLContext)} and + * {@link #set(BindingSetStatementContext)}, i.e. when bind variables are + * inlined, then they must not be bound to the {@link PreparedStatement} in + * {@link #set(BindingSetStatementContext)} + * + * @param ctx The context object containing all argument objects. + * @throws SQLException Implementations are allowed to pass on all + * {@link SQLException}s to the caller to be wrapped in + * {@link DataAccessException}s. + */ + void sql(BindingSQLContext ctx) throws SQLException; + + /** + * Register a {@link CallableStatement}'s OUT parameter. + * + * @param ctx The context object containing all argument objects. + * @throws SQLException Implementations are allowed to pass on all + * {@link SQLException}s to the caller to be wrapped in + * {@link DataAccessException}s. + */ + void register(BindingRegisterContext ctx) throws SQLException; + + /** + * Set a {@link PreparedStatement}'s IN parameter. + * + * @param ctx The context object containing all argument objects. + * @throws SQLException Implementations are allowed to pass on all + * {@link SQLException}s to the caller to be wrapped in + * {@link DataAccessException}s. + */ + void set(BindingSetStatementContext ctx) throws SQLException; + + /** + * Set a {@link SQLOutput}'s IN parameter. + * + * @param ctx The context object containing all argument objects. + * @throws SQLException Implementations are allowed to pass on all + * {@link SQLException}s to the caller to be wrapped in + * {@link DataAccessException}s. + */ + void set(BindingSetSQLOutputContext ctx) throws SQLException; + + /** + * Get a {@link ResultSet}'s OUT value. + *

+ * Implementations are expected to produce a value by calling + * {@link BindingGetResultSetContext#value(Object)}, passing the resulting + * value to the method. + * + * @param ctx The context object containing all argument objects. + * @throws SQLException Implementations are allowed to pass on all + * {@link SQLException}s to the caller to be wrapped in + * {@link DataAccessException}s. + */ + void get(BindingGetResultSetContext ctx) throws SQLException; + + /** + * Get a {@link CallableStatement}'s OUT value. + *

+ * Implementations are expected to produce a value by calling + * {@link BindingGetStatementContext#value(Object)}, passing the resulting + * value to the method. + * + * @param ctx The context object containing all argument objects. + * @throws SQLException Implementations are allowed to pass on all + * {@link SQLException}s to the caller to be wrapped in + * {@link DataAccessException}s. + */ + void get(BindingGetStatementContext ctx) throws SQLException; + + /** + * Get a {@link SQLInput}'s OUT value. + *

+ * Implementations are expected to produce a value by calling + * {@link BindingGetSQLInputContext#value(Object)}, passing the resulting + * value to the method. + * + * @param ctx The context object containing all argument objects. + * @throws SQLException Implementations are allowed to pass on all + * {@link SQLException}s to the caller to be wrapped in + * {@link DataAccessException}s. + */ + void get(BindingGetSQLInputContext ctx) throws SQLException; +} diff --git a/jOOQ/src/main/java/org/jooq/BindingContext.java b/jOOQ/src/main/java/org/jooq/BindingContext.java new file mode 100644 index 0000000000..80a2fc41a9 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/BindingContext.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +public interface BindingContext { + Configuration configuration(); + + SQLDialect dialect(); + + SQLDialect family(); +} \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/BindingGetResultSetContext.java b/jOOQ/src/main/java/org/jooq/BindingGetResultSetContext.java new file mode 100644 index 0000000000..c2d88e10c9 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/BindingGetResultSetContext.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +import java.sql.ResultSet; + +/** + * A container type for {@link Binding#get(BindingGetResultSetContext)} + * arguments. + * + * @author Lukas Eder + */ +public interface BindingGetResultSetContext extends Scope { + + /** + * The {@link ResultSet} from which a value is retrieved. + */ + ResultSet resultSet(); + + /** + * The column index at which the value is retrieved. + */ + int index(); + + /** + * A callback to which the resulting value is registered. + */ + void value(T value); +} diff --git a/jOOQ/src/main/java/org/jooq/BindingGetSQLInputContext.java b/jOOQ/src/main/java/org/jooq/BindingGetSQLInputContext.java new file mode 100644 index 0000000000..dbcf79d5d3 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/BindingGetSQLInputContext.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +import java.sql.SQLInput; + +/** + * A container type for {@link Binding#get(BindingGetSQLInputContext)} + * arguments. + * + * @author Lukas Eder + */ +public interface BindingGetSQLInputContext extends Scope { + + /** + * The {@link SQLInput} from which a value is retrieved. + */ + SQLInput input(); + + /** + * A callback to which the resulting value is registered. + */ + void value(T value); +} diff --git a/jOOQ/src/main/java/org/jooq/BindingGetStatementContext.java b/jOOQ/src/main/java/org/jooq/BindingGetStatementContext.java new file mode 100644 index 0000000000..e29b5d4b24 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/BindingGetStatementContext.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +import java.sql.CallableStatement; + +/** + * A container type for {@link Binding#get(BindingGetStatementContext)} + * arguments. + * + * @author Lukas Eder + */ +public interface BindingGetStatementContext extends Scope { + + /** + * The {@link CallableStatement} from which a value is retrieved. + */ + CallableStatement statement(); + + /** + * The column index at which the value is retrieved. + */ + int index(); + + /** + * A callback to which the resulting value is registered. + */ + void value(T value); +} diff --git a/jOOQ/src/main/java/org/jooq/BindingRegisterContext.java b/jOOQ/src/main/java/org/jooq/BindingRegisterContext.java new file mode 100644 index 0000000000..2e9666e3d1 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/BindingRegisterContext.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +import java.sql.CallableStatement; + +/** + * A container type for {@link Binding#register(BindingRegisterContext)} + * arguments. + * + * @author Lukas Eder + */ +public interface BindingRegisterContext extends Scope { + + /** + * The {@link CallableStatement} on which a bind variable should be + * registered. + */ + CallableStatement statement(); + + /** + * The bind variable index at which a bind variable should be registered. + */ + int index(); +} diff --git a/jOOQ/src/main/java/org/jooq/BindingSQLContext.java b/jOOQ/src/main/java/org/jooq/BindingSQLContext.java new file mode 100644 index 0000000000..94bec36ca2 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/BindingSQLContext.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +/** + * A container type for {@link Binding#sql(BindingSQLContext)} arguments. + * + * @author Lukas Eder + */ +public interface BindingSQLContext extends Scope { + + /** + * The {@link RenderContext} that contains the generated SQL and the current + * SQL generation state. + */ + RenderContext render(); + + /** + * The bind value that is being rendered. + */ + T value(); +} diff --git a/jOOQ/src/main/java/org/jooq/BindingSetSQLOutputContext.java b/jOOQ/src/main/java/org/jooq/BindingSetSQLOutputContext.java new file mode 100644 index 0000000000..789261d9c2 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/BindingSetSQLOutputContext.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +import java.sql.SQLOutput; + +/** + * A container type for {@link Binding#set(BindingSetSQLOutputContext)} + * arguments. + * + * @author Lukas Eder + */ +public interface BindingSetSQLOutputContext extends Scope { + + /** + * The {@link SQLOutput} to which a bind variable should be bound. + */ + SQLOutput output(); + + /** + * The bind value that is being bound. + */ + T value(); +} diff --git a/jOOQ/src/main/java/org/jooq/BindingSetStatementContext.java b/jOOQ/src/main/java/org/jooq/BindingSetStatementContext.java new file mode 100644 index 0000000000..58dd8d2c76 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/BindingSetStatementContext.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +import java.sql.PreparedStatement; + +/** + * A container type for {@link Binding#set(BindingSetStatementContext)} + * arguments. + * + * @author Lukas Eder + */ +public interface BindingSetStatementContext extends Scope { + + /** + * The {@link PreparedStatement} to which a bind variable should be bound. + */ + PreparedStatement statement(); + + /** + * The bind variable index at which a bind variable should be bound. + */ + int index(); + + /** + * The bind value that is being bound. + */ + T value(); +} diff --git a/jOOQ/src/main/java/org/jooq/Context.java b/jOOQ/src/main/java/org/jooq/Context.java index 4e758cf4e3..8ecf7111aa 100644 --- a/jOOQ/src/main/java/org/jooq/Context.java +++ b/jOOQ/src/main/java/org/jooq/Context.java @@ -41,7 +41,6 @@ package org.jooq; import java.sql.PreparedStatement; -import java.util.Map; import org.jooq.RenderContext.CastMode; import org.jooq.conf.ParamType; @@ -56,93 +55,12 @@ import org.jooq.exception.DataAccessException; * @see BindContext * @see RenderContext */ -public interface Context> { +public interface Context> extends Scope { // ------------------------------------------------------------------------ // General methods // ------------------------------------------------------------------------ - /** - * The configuration wrapped by this context. - */ - Configuration configuration(); - - /** - * The settings wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().settings(). - */ - Settings settings(); - - /** - * The {@link SQLDialect} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect(). - */ - SQLDialect dialect(); - - /** - * The {@link SQLDialect#family()} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect().family(). - */ - SQLDialect family(); - - /** - * Get all custom data from this Context. - *

- * This is custom data that was previously set to the context using - * {@link #data(Object, Object)}. Use custom data if you want to pass data - * to {@link QueryPart} objects for a given {@link RenderContext} or - * {@link BindContext}. - *

- * Unlike {@link Configuration#data()}, these data's lifecycle only - * matches that of a render or bind context. - * - * @return The custom data. This is never null - */ - Map data(); - - /** - * Get some custom data from this Context. - *

- * This is custom data that was previously set to the context using - * {@link #data(Object, Object)}. Use custom data if you want to pass data - * to {@link QueryPart} objects for a given {@link RenderContext} or - * {@link BindContext}. - *

- * Unlike {@link Configuration#data()}, these data's lifecycle only - * matches that of a render or bind context. - * - * @param key A key to identify the custom data - * @return The custom data or null if no such data is contained - * in this ExecuteContext - * @see ExecuteListener - */ - Object data(Object key); - - /** - * Set some custom data to this Context. - *

- * This is custom data that was previously set to the context using - * {@link #data(Object, Object)}. Use custom data if you want to pass data - * to {@link QueryPart} objects for a given {@link RenderContext} or - * {@link BindContext}. - *

- * Unlike {@link Configuration#data()}, these data's lifecycle only - * matches that of a render or bind context. - * - * @param key A key to identify the custom data - * @param value The custom data - * @return The previously set custom data or null if no data - * was previously set for the given key - * @see ExecuteListener - */ - Object data(Object key, Object value); - /** * Visit a QueryPart in the current Context. *

diff --git a/jOOQ/src/main/java/org/jooq/DSLContext.java b/jOOQ/src/main/java/org/jooq/DSLContext.java index 86a5ac8835..bb06977955 100644 --- a/jOOQ/src/main/java/org/jooq/DSLContext.java +++ b/jOOQ/src/main/java/org/jooq/DSLContext.java @@ -113,42 +113,12 @@ import org.jooq.impl.DSL; * @see Configuration * @author Lukas Eder */ -public interface DSLContext { +public interface DSLContext extends Scope { // ------------------------------------------------------------------------- // XXX Configuration API // ------------------------------------------------------------------------- - /** - * The Configuration referenced from this - * DSLContext. - */ - Configuration configuration(); - - /** - * The settings wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().settings(). - */ - Settings settings(); - - /** - * The {@link SQLDialect} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect(). - */ - SQLDialect dialect(); - - /** - * The {@link SQLDialect#family()} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect().family(). - */ - SQLDialect family(); - /** * Map a schema to another one. *

diff --git a/jOOQ/src/main/java/org/jooq/ExecuteContext.java b/jOOQ/src/main/java/org/jooq/ExecuteContext.java index efce65db5a..b72eea388a 100644 --- a/jOOQ/src/main/java/org/jooq/ExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/ExecuteContext.java @@ -58,7 +58,7 @@ import org.jooq.exception.DataAccessException; * @author Lukas Eder * @see ExecuteListener */ -public interface ExecuteContext { +public interface ExecuteContext extends Scope { /** * Get all custom data from this ExecuteContext. @@ -110,35 +110,6 @@ public interface ExecuteContext { */ Object data(Object key, Object value); - /** - * The configuration wrapped by this context. - */ - Configuration configuration(); - - /** - * The settings wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().settings(). - */ - Settings settings(); - - /** - * The {@link SQLDialect} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect(). - */ - SQLDialect dialect(); - - /** - * The {@link SQLDialect#family()} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect().family(). - */ - SQLDialect family(); - /** * The connection to be used in this execute context. *

diff --git a/jOOQ/src/main/java/org/jooq/Field.java b/jOOQ/src/main/java/org/jooq/Field.java index 5d17f56ba1..58c9037d0f 100644 --- a/jOOQ/src/main/java/org/jooq/Field.java +++ b/jOOQ/src/main/java/org/jooq/Field.java @@ -116,6 +116,11 @@ public interface Field extends GroupField { */ Converter getConverter(); + /** + * The field's underlying {@link Binding}. + */ + Binding getBinding(); + /** * The Java type of the field. */ diff --git a/jOOQ/src/main/java/org/jooq/Parameter.java b/jOOQ/src/main/java/org/jooq/Parameter.java index 0810292b4b..126301224c 100644 --- a/jOOQ/src/main/java/org/jooq/Parameter.java +++ b/jOOQ/src/main/java/org/jooq/Parameter.java @@ -69,6 +69,11 @@ public interface Parameter extends QueryPart { */ Converter getConverter(); + /** + * The parameter's underlying {@link Binding}. + */ + Binding getBinding(); + /** * The type of this parameter (might not be dialect-specific) */ diff --git a/jOOQ/src/main/java/org/jooq/RecordContext.java b/jOOQ/src/main/java/org/jooq/RecordContext.java index 433610bd74..6be8b27054 100644 --- a/jOOQ/src/main/java/org/jooq/RecordContext.java +++ b/jOOQ/src/main/java/org/jooq/RecordContext.java @@ -42,15 +42,13 @@ package org.jooq; import java.util.Map; -import org.jooq.conf.Settings; - /** * A context object for {@link Record} manipulation passed to registered * {@link RecordListener}'s. * * @author Lukas Eder */ -public interface RecordContext { +public interface RecordContext extends Scope { /** * Get all custom data from this RecordContext. @@ -102,35 +100,6 @@ public interface RecordContext { */ Object data(Object key, Object value); - /** - * The configuration wrapped by this context. - */ - Configuration configuration(); - - /** - * The settings wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().settings(). - */ - Settings settings(); - - /** - * The {@link SQLDialect} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect(). - */ - SQLDialect dialect(); - - /** - * The {@link SQLDialect#family()} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect().family(). - */ - SQLDialect family(); - /** * The type of database interaction that is being executed. *

diff --git a/jOOQ/src/main/java/org/jooq/Scope.java b/jOOQ/src/main/java/org/jooq/Scope.java new file mode 100644 index 0000000000..43eae0ae17 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Scope.java @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +import java.util.Map; + +import org.jooq.conf.Settings; + +/** + * Scope implementations provide access to a variety of objects that are + * available from a given scope. + *

+ * The scope of the various objects contained in this type (e.g. + * {@link #configuration()}, {@link #settings()}, etc.) are implementation + * dependent and will be specified by the concrete subtype of Scope. + * + * @author Lukas Eder + */ +public interface Scope { + + /** + * The configuration of the current scope. + */ + Configuration configuration(); + + /** + * The settings wrapped by this context. + *

+ * This method is a convenient way of accessing + * configuration().settings(). + */ + Settings settings(); + + /** + * The {@link SQLDialect} wrapped by this context. + *

+ * This method is a convenient way of accessing + * configuration().dialect(). + */ + SQLDialect dialect(); + + /** + * The {@link SQLDialect#family()} wrapped by this context. + *

+ * This method is a convenient way of accessing + * configuration().dialect().family(). + */ + SQLDialect family(); + + /** + * Get all custom data from this Scope. + *

+ * This is custom data that was previously set to the context using + * {@link #data(Object, Object)}. Use custom data if you want to pass data + * to {@link QueryPart} objects for a given {@link Scope}. + * + * @return The custom data. This is never null + */ + Map data(); + + /** + * Get some custom data from this Scope. + *

+ * This is custom data that was previously set to the context using + * {@link #data(Object, Object)}. Use custom data if you want to pass data + * to {@link QueryPart} objects for a given {@link Scope} + * + * @param key A key to identify the custom data + * @return The custom data or null if no such data is contained + * in this Scope + */ + Object data(Object key); + + /** + * Set some custom data to this Scope. + *

+ * This is custom data that was previously set to the context using + * {@link #data(Object, Object)}. Use custom data if you want to pass data + * to {@link QueryPart} objects for a given {@link Scope}. + * + * @param key A key to identify the custom data + * @param value The custom data + * @return The previously set custom data or null if no data + * was previously set for the given key + */ + Object data(Object key, Object value); +} diff --git a/jOOQ/src/main/java/org/jooq/TransactionContext.java b/jOOQ/src/main/java/org/jooq/TransactionContext.java index 1e15cb2a09..4a0b67e8c6 100644 --- a/jOOQ/src/main/java/org/jooq/TransactionContext.java +++ b/jOOQ/src/main/java/org/jooq/TransactionContext.java @@ -40,7 +40,6 @@ */ package org.jooq; -import org.jooq.conf.Settings; /** * A context object that is used to pass arguments to the various methods of @@ -48,36 +47,7 @@ import org.jooq.conf.Settings; * * @author Lukas Eder */ -public interface TransactionContext { - - /** - * The configuration scoped to this transaction and its nested transactions. - */ - Configuration configuration(); - - /** - * The settings wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().settings(). - */ - Settings settings(); - - /** - * The {@link SQLDialect} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect(). - */ - SQLDialect dialect(); - - /** - * The {@link SQLDialect#family()} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect().family(). - */ - SQLDialect family(); +public interface TransactionContext extends Scope { /** * A user-defined transaction object, possibly obtained from diff --git a/jOOQ/src/main/java/org/jooq/VisitContext.java b/jOOQ/src/main/java/org/jooq/VisitContext.java index 02690367c1..4c4f312375 100644 --- a/jOOQ/src/main/java/org/jooq/VisitContext.java +++ b/jOOQ/src/main/java/org/jooq/VisitContext.java @@ -42,8 +42,6 @@ package org.jooq; import java.util.Map; -import org.jooq.conf.Settings; - /** * A context object for {@link QueryPart} traversal passed to registered * {@link VisitListener}'s. @@ -51,7 +49,7 @@ import org.jooq.conf.Settings; * @author Lukas Eder * @see VisitListener */ -public interface VisitContext { +public interface VisitContext extends Scope { /** * Get all custom data from this VisitContext. @@ -91,35 +89,6 @@ public interface VisitContext { */ Object data(Object key, Object value); - /** - * The configuration wrapped by this context. - */ - Configuration configuration(); - - /** - * The settings wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().settings(). - */ - Settings settings(); - - /** - * The {@link SQLDialect} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect(). - */ - SQLDialect dialect(); - - /** - * The {@link SQLDialect#family()} wrapped by this context. - *

- * This method is a convenient way of accessing - * configuration().dialect().family(). - */ - SQLDialect family(); - /** * The most recent clause that was encountered through * {@link Context#start(Clause)}. diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractBindingContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractBindingContext.java new file mode 100644 index 0000000000..ee520b43e4 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractBindingContext.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import org.jooq.BindingContext; +import org.jooq.Configuration; +import org.jooq.SQLDialect; + +/** + * @author Lukas Eder + */ +abstract class AbstractBindingContext implements BindingContext { + + private final Configuration configuration; + + AbstractBindingContext(Configuration configuration) { + this.configuration = configuration; + } + + @Override + public final Configuration configuration() { + return configuration; + } + + @Override + public final SQLDialect dialect() { + return configuration.dialect(); + } + + @Override + public final SQLDialect family() { + return configuration.dialect().family(); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java index 622c4c1ee1..68b91ba0a3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java @@ -46,7 +46,6 @@ import static org.jooq.impl.Utils.DATA_OMIT_CLAUSE_EVENT_EMISSION; import java.sql.PreparedStatement; import java.util.ArrayDeque; import java.util.Deque; -import java.util.HashMap; import java.util.Map; import org.jooq.BindContext; @@ -69,11 +68,9 @@ import org.jooq.conf.Settings; * @author Lukas Eder */ @SuppressWarnings("unchecked") -abstract class AbstractContext> implements Context { +abstract class AbstractContext> extends AbstractScope implements Context { - final Configuration configuration; final PreparedStatement stmt; - final Map data; boolean declareFields; boolean declareTables; @@ -94,9 +91,9 @@ abstract class AbstractContext> implements Context { CastMode castMode = CastMode.DEFAULT; AbstractContext(Configuration configuration, PreparedStatement stmt) { - this.configuration = configuration; + super(configuration); + this.stmt = stmt; - this.data = new HashMap(); this.visitClauses = new ArrayDeque(); VisitListenerProvider[] providers = configuration.visitListenerProviders(); @@ -316,41 +313,6 @@ abstract class AbstractContext> implements Context { // XXX Context API // ------------------------------------------------------------------------ - @Override - public final Configuration configuration() { - return configuration; - } - - @Override - public final Settings settings() { - return Utils.settings(configuration()); - } - - @Override - public final SQLDialect dialect() { - return Utils.configuration(configuration()).dialect(); - } - - @Override - public final SQLDialect family() { - return dialect().family(); - } - - @Override - public final Map data() { - return data; - } - - @Override - public final Object data(Object key) { - return data.get(key); - } - - @Override - public final Object data(Object key, Object value) { - return data.put(key, value); - } - private final C visit0(QueryPart part) { if (part != null) { QueryPartInternal internal = (QueryPartInternal) part; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index 24982d79fb..055cf5d8f9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -77,6 +77,7 @@ import java.util.Map; import java.util.Map.Entry; import org.jooq.BetweenAndStep; +import org.jooq.Binding; import org.jooq.CaseValueStep; import org.jooq.CaseWhenStep; import org.jooq.Clause; @@ -113,6 +114,7 @@ abstract class AbstractField extends AbstractQueryPart implements Field { private final String comment; private final DataType dataType; private final Converter converter; + private final Binding binding; AbstractField(String name, DataType type) { this(name, type, null, null); @@ -131,6 +133,8 @@ abstract class AbstractField extends AbstractQueryPart implements Field { : type instanceof ConvertedDataType ? ((ConvertedDataType) type).converter() : new IdentityConverter(type.getType()); + + this.binding = new DefaultBinding(this.converter, type.isLob()); } // ------------------------------------------------------------------------ @@ -174,6 +178,11 @@ abstract class AbstractField extends AbstractQueryPart implements Field { return converter; } + @Override + public final Binding getBinding() { + return binding; + } + @Override public final DataType getDataType() { return dataType; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index bed7a39b89..115ed06247 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -57,7 +57,6 @@ import static org.jooq.impl.Utils.settings; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.SQLException; -import java.sql.Types; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -67,7 +66,6 @@ import java.util.Map; import java.util.Set; import org.jooq.AggregateFunction; -// ... import org.jooq.AttachableInternal; import org.jooq.BindContext; import org.jooq.Clause; @@ -86,7 +84,6 @@ import org.jooq.Result; import org.jooq.Routine; import org.jooq.Schema; import org.jooq.UDTField; -import org.jooq.UDTRecord; import org.jooq.exception.ControlFlowSignal; import org.jooq.tools.Convert; @@ -559,13 +556,22 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro if (parameter.equals(getReturnParameter()) || getOutParameters().contains(parameter)) { - int index = parameterIndexes.get(parameter); - results.put(parameter, Utils.getFromStatement(ctx, parameter, index)); + fetchOutParameter(ctx, parameter); } } } - @SuppressWarnings("unchecked") + private final void fetchOutParameter(ExecuteContext ctx, Parameter parameter) throws SQLException { + DefaultBindingGetStatementContext out = new DefaultBindingGetStatementContext( + ctx.configuration(), + (CallableStatement) ctx.statement(), + parameterIndexes.get(parameter) + ); + + parameter.getBinding().get(out); + results.put(parameter, out.value()); + } + private final void registerOutParameters(Configuration c, CallableStatement statement) throws SQLException { // Register all out / inout parameters according to their position @@ -574,47 +580,15 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro if (parameter.equals(getReturnParameter()) || getOutParameters().contains(parameter)) { - int index = parameterIndexes.get(parameter); - int sqlType = parameter.getDataType().getDataType(c).getSQLType(); - - switch (c.dialect().family()) { - /* [pro] xx - - xx xxx xxxx xxxx xxxxxxx xxxxx xxxxxx xxxxx xx xxxx - xx xxxx xxx xxxx xxxx - xxxx xxxxxxx x - xx xxxxxxxx xx xxxxxxxxxxxxx x - xxxxxxxxxxxx xxxxxx x xxxxx - xxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxx - x - - xxxx xx xxxxxxxx xx xxxxxxxxxxxx x - xxxxxxxxxxxxxx xxxxxx x xxxxxxxxxxxxxxxxxxxxx - xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxxxxxxxx - x - - xx xxx xxxxxxx xxxxxxxxx xx xxx xx xxxxxxxx x xxxx - xx xxxxxxx - xxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx - x - - xxxxxx - x - - xx [/pro] */ - default: { - statement.registerOutParameter(index, sqlType); - break; - } - } + registerOutParameter(c, statement, parameter); } } } + private final void registerOutParameter(Configuration c, CallableStatement statement, Parameter parameter) throws SQLException { + parameter.getBinding().register(new DefaultBindingRegisterContext(c, statement, parameterIndexes.get(parameter))); + } + // ------------------------------------------------------------------------ // Fetch routine results // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractScope.java b/jOOQ/src/main/java/org/jooq/impl/AbstractScope.java new file mode 100644 index 0000000000..3ecbdb6f28 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractScope.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import java.util.HashMap; +import java.util.Map; + +import org.jooq.Configuration; +import org.jooq.SQLDialect; +import org.jooq.Scope; +import org.jooq.conf.Settings; + +/** + * @author Lukas Eder + */ +abstract class AbstractScope implements Scope { + + private final Configuration configuration; + private final Map data; + + AbstractScope(Configuration configuration) { + this.data = new HashMap(); + + // The Configuration can be null when unattached objects are + // executed or when unattached Records are stored... + if (configuration == null) { + configuration = new DefaultConfiguration(); + } + + this.configuration = configuration; + } + + // ------------------------------------------------------------------------ + // XXX Scope API + // ------------------------------------------------------------------------ + + @Override + public final Configuration configuration() { + return configuration; + } + + @Override + public final Settings settings() { + return Utils.settings(configuration()); + } + + @Override + public final SQLDialect dialect() { + return Utils.configuration(configuration()).dialect(); + } + + @Override + public final SQLDialect family() { + return dialect().family(); + } + + @Override + public final Map data() { + return data; + } + + @Override + public final Object data(Object key) { + return data.get(key); + } + + @Override + public final Object data(Object key, Object value) { + return data.put(key, value); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/ArrayRecordImpl.java index ee904d0ad7..3fbd9d18b8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ArrayRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayRecordImpl.java @@ -183,7 +183,7 @@ xxxxxx xxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxx xxxxxxxxxxx xxxxxx xxxxx xxxx xxxxxxxxxxxxxxxxxx xxxxxx xxxxxx xxxxxxxxxxxx x - xxxxxxxxxxxxxxx xxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java index 8851136434..97567bf2a1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java @@ -1471,7 +1471,9 @@ class CursorImpl implements Cursor { * Utility method to prevent unnecessary unchecked conversions */ private final void setValue(AbstractRecord record, Field field, int index) throws SQLException { - T value = Utils.getFromResultSet(ctx, field, index + 1); + DefaultBindingGetResultSetContext out = new DefaultBindingGetResultSetContext(ctx.configuration(), ctx.resultSet(), index + 1); + field.getBinding().get(out); + T value = out.value(); record.values[index] = value; record.originals[index] = value; diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultArray.java b/jOOQ/src/main/java/org/jooq/impl/DefaultArray.java new file mode 100644 index 0000000000..57a57ab505 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultArray.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import java.sql.Array; +import java.sql.ResultSet; +import java.util.Map; + +import org.jooq.SQLDialect; +import org.jooq.exception.SQLDialectNotSupportedException; + +class DefaultArray implements Array { + + private final SQLDialect dialect; + private final Object[] array; + private final Class type; + + public DefaultArray(SQLDialect dialect, Object[] array, Class type) { + this.dialect = dialect; + this.array = array; + this.type = type; + } + + @Override + public String getBaseTypeName() { + return DefaultDataType.getDataType(dialect, type.getComponentType()).getTypeName(); + } + + @Override + public int getBaseType() { + throw new SQLDialectNotSupportedException("Array.getBaseType()"); + } + + @Override + public Object getArray() { + return array; + } + + @Override + public Object getArray(Map> map) { + return array; + } + + @Override + public Object getArray(long index, int count) { + throw new SQLDialectNotSupportedException("Array.getArray(long, int)"); + } + + @Override + public Object getArray(long index, int count, Map> map) { + throw new SQLDialectNotSupportedException("Array.getArray(long, int, Map)"); + } + + @Override + public ResultSet getResultSet() { + throw new SQLDialectNotSupportedException("Array.getResultSet()"); + } + + @Override + public ResultSet getResultSet(Map> map) { + throw new SQLDialectNotSupportedException("Array.getResultSet(Map)"); + } + + @Override + public ResultSet getResultSet(long index, int count) { + throw new SQLDialectNotSupportedException("Array.getResultSet(long, int)"); + } + + @Override + public ResultSet getResultSet(long index, int count, Map> map) { + throw new SQLDialectNotSupportedException("Array.getResultSet(long, int, Map)"); + } + + @Override + public void free() { + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java index de080e3058..e899666ab9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java @@ -40,364 +40,28 @@ */ package org.jooq.impl; -import static java.util.Arrays.asList; -// ... -// ... -// ... -import static org.jooq.SQLDialect.POSTGRES; -import static org.jooq.SQLDialect.SQLITE; -// ... -// ... -import static org.jooq.impl.DefaultExecuteContext.localTargetConnection; -import static org.jooq.tools.reflect.Reflect.on; -import static org.jooq.util.postgres.PostgresUtils.toPGArrayString; -import static org.jooq.util.postgres.PostgresUtils.toPGInterval; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Date; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.sql.Time; -import java.sql.Timestamp; -import java.sql.Types; -import java.util.Arrays; -import java.util.UUID; -// ... import org.jooq.BindContext; import org.jooq.Configuration; -import org.jooq.Converter; -import org.jooq.EnumType; import org.jooq.Field; -import org.jooq.SQLDialect; -import org.jooq.UDTRecord; -import org.jooq.exception.SQLDialectNotSupportedException; -import org.jooq.tools.Convert; -import org.jooq.tools.JooqLogger; -import org.jooq.tools.jdbc.MockArray; -import org.jooq.types.DayToSecond; -import org.jooq.types.UByte; -import org.jooq.types.UInteger; -import org.jooq.types.ULong; -import org.jooq.types.UShort; -import org.jooq.types.YearToMonth; /** * @author Lukas Eder */ class DefaultBindContext extends AbstractBindContext { - private static final JooqLogger log = JooqLogger.getLogger(DefaultBindContext.class); - DefaultBindContext(Configuration configuration, PreparedStatement stmt) { super(configuration, stmt); } @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked" }) protected final BindContext bindValue0(Object value, Field field) throws SQLException { - SQLDialect dialect = configuration.dialect(); - - // [#650] [#3108] Use the Field's Converter before actually binding any value - Converter converter = field.getConverter(); - Class type = converter.fromType(); - value = ((Converter) converter).to(value); - - if (log.isTraceEnabled()) { - if (value != null && value.getClass().isArray() && value.getClass() != byte[].class) { - log.trace("Binding variable " + peekIndex(), Arrays.asList((Object[]) value) + " (" + type + ")"); - } - else { - log.trace("Binding variable " + peekIndex(), value + " (" + type + ")"); - } - } - - // Setting null onto a prepared statement is subtly different for every - // SQL dialect. See the following section for details - if (value == null) { - int sqlType = DefaultDataType.getDataType(dialect, type).getSQLType(); - - /* [pro] xx - xx xxxxxxxxxxxx xxxxx xxxxx xxxx xx xx xxxxx xxxx xxxxx xxxx xxxx - xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxx xxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxxxxx - x - - xxxx - xx [/pro] */ - // [#1126] Oracle's UDTs need to be bound with their type name - if (UDTRecord.class.isAssignableFrom(type)) { - String typeName = Utils.newRecord(false, (Class>) type) - .operate(null) - .getUDT() - .getName(); - stmt.setNull(nextIndex(), sqlType, typeName); - } - - // [#1225] [#1227] TODO Put this logic into DataType - // Some dialects have trouble binding binary data as BLOB - else if (asList(POSTGRES).contains(configuration.dialect()) && sqlType == Types.BLOB) { - stmt.setNull(nextIndex(), Types.BINARY); - } - - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxx x - - xx xxxx xxxxxxxxxx xxxx xx xxxx xxxxxx xxxx xxx xxx xxxxxxxxx xxxxxx - xx xxxxxxxxxxx xxxxx xxxxxxx xxx xxxxxxx - xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xxxxxx xxxxxxxxx x - xxxx xxxxxxxxxxxxx - xxxx xxxxxxxxxxxxxxxx - xxxx xxxxxxxxxxxxxxxxxxxx - xxxx xxxxxxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx - xxxxxx - - xxxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxx - xxxxxx - x - x - - xx xxxxxxx xxxxxxx xxx xxx xxxxxx xxxxxx xxxx xxx xxxx xxxxxxxx xxx xxx xxxx xxxxxx - xx xxxxxx xxx xxx xxxxxxx xxxxx - xxxx xx xxxxxxxx xx xxxxxxxxxxxxx xx xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxx - x - - xx [/pro] */ - // All other types can be set to null if the JDBC type is known - else if (sqlType != Types.OTHER) { - stmt.setNull(nextIndex(), sqlType); - } - - /* [pro] xx - xx xxxxxx xxx xxx xxxxxxx xxxxxxx xxxxx xxxxxx xx xxx xx xxxx - xx xxxxxxxxxxx xxx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx - x - - xx xxxxxx xxx xxxxxxx xxxxxxx xxxxx xxx xx xxx xx xxxx xxxxx xxxxxxx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx - x - - xx [/pro] */ - // [#729] In the absence of the correct JDBC type, try setObject - else { - stmt.setObject(nextIndex(), null); - } - } - else { - - // Try to infer the bind value type from the actual bind value if possible. - if (type == Object.class) { - type = value.getClass(); - } - - if (type == Blob.class) { - stmt.setBlob(nextIndex(), (Blob) value); - } - else if (type == Boolean.class) { - /* [pro] xx - xx xx xxxxxx xxxxxx xxxxxx xx xxxxx xxxxx xxxxx xx xxxxxxxxx xx xxxxxxxxxx xx xxxxxxx xxxxxxx - xx xxxxxxxxxxxxxxxxx xx xxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx xxxxx x x x xxx - xxxx - xx [/pro] */ - stmt.setBoolean(nextIndex(), (Boolean) value); - } - else if (type == BigDecimal.class) { - if (asList(SQLITE).contains(dialect.family())) { - stmt.setString(nextIndex(), value.toString()); - } - else { - stmt.setBigDecimal(nextIndex(), (BigDecimal) value); - } - } - else if (type == BigInteger.class) { - if (asList(SQLITE).contains(dialect.family())) { - stmt.setString(nextIndex(), value.toString()); - } - else { - stmt.setBigDecimal(nextIndex(), new BigDecimal((BigInteger) value)); - } - } - else if (type == Byte.class) { - stmt.setByte(nextIndex(), (Byte) value); - } - else if (type == byte[].class) { - stmt.setBytes(nextIndex(), (byte[]) value); - } - else if (type == Clob.class) { - stmt.setClob(nextIndex(), (Clob) value); - } - else if (type == Double.class) { - stmt.setDouble(nextIndex(), (Double) value); - } - else if (type == Float.class) { - stmt.setFloat(nextIndex(), (Float) value); - } - else if (type == Integer.class) { - stmt.setInt(nextIndex(), (Integer) value); - } - else if (type == Long.class) { - /* [pro] xx - xx xxxxxxxxxxxxxxxxx xx xxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx - xxxx - xx [/pro] */ - stmt.setLong(nextIndex(), (Long) value); - } - else if (type == Short.class) { - stmt.setShort(nextIndex(), (Short) value); - } - else if (type == String.class) { - stmt.setString(nextIndex(), (String) value); - } - - // There is potential for trouble when binding date time as such - // ------------------------------------------------------------- - else if (type == Date.class) { - if (dialect == SQLITE) { - stmt.setString(nextIndex(), ((Date) value).toString()); - } - else { - stmt.setDate(nextIndex(), (Date) value); - } - } - else if (type == Time.class) { - if (dialect == SQLITE) { - stmt.setString(nextIndex(), ((Time) value).toString()); - } - else { - stmt.setTime(nextIndex(), (Time) value); - } - } - else if (type == Timestamp.class) { - if (dialect == SQLITE) { - stmt.setString(nextIndex(), ((Timestamp) value).toString()); - } - else { - stmt.setTimestamp(nextIndex(), (Timestamp) value); - } - } - - // [#566] Interval data types are best bound as Strings - else if (type == YearToMonth.class) { - if (dialect == POSTGRES) { - stmt.setObject(nextIndex(), toPGInterval((YearToMonth) value)); - } - else { - stmt.setString(nextIndex(), value.toString()); - } - } - else if (type == DayToSecond.class) { - if (dialect == POSTGRES) { - stmt.setObject(nextIndex(), toPGInterval((DayToSecond) value)); - } - else { - stmt.setString(nextIndex(), value.toString()); - } - } - else if (type == UByte.class) { - stmt.setShort(nextIndex(), ((UByte) value).shortValue()); - } - else if (type == UShort.class) { - stmt.setInt(nextIndex(), ((UShort) value).intValue()); - } - else if (type == UInteger.class) { - /* [pro] xx - xx xxxxxxxxxxxxxxxxx xx xxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx - xxxx - xx [/pro] */ - stmt.setLong(nextIndex(), ((UInteger) value).longValue()); - } - else if (type == ULong.class) { - /* [pro] xx - xx xxxxxxxxxxxxxxxxx xx xxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx - xxxx - xx [/pro] */ - stmt.setBigDecimal(nextIndex(), new BigDecimal(value.toString())); - } - else if (type == UUID.class) { - switch (dialect.family()) { - - // [#1624] Some JDBC drivers natively support the - // java.util.UUID data type - case H2: - case POSTGRES: { - stmt.setObject(nextIndex(), value); - break; - } - - /* [pro] xx - xx xxxxx xxx xxxxxxxx xxxx xxxx xxxxx xx xx xxxx xxxx xxxxxxxx - xx xxxx xx xxxx xxxxxxxxxx xxxxxxx xxxx xxxxxxxxxxxxxxxxxx - xxxx xxxxxxxxxx - xxxx xxxxxxx - - xx [/pro] */ - // Most databases don't have such a type. In this case, jOOQ - // simulates the type - default: { - stmt.setString(nextIndex(), value.toString()); - break; - } - } - } - - // The type byte[] is handled earlier. byte[][] can be handled here - else if (type.isArray()) { - switch (dialect) { - case POSTGRES: { - stmt.setString(nextIndex(), toPGArrayString((Object[]) value)); - break; - } - case HSQLDB: { - Object[] a = (Object[]) value; - Class t = type; - - // [#2325] Some array types are not natively supported by HSQLDB - // More integration tests are probably needed... - if (type == UUID[].class) { - a = Convert.convertArray(a, String[].class); - t = String[].class; - } - - stmt.setArray(nextIndex(), new MockArray(dialect, a, t)); - break; - } - case H2: { - stmt.setObject(nextIndex(), value); - break; - } - default: - throw new SQLDialectNotSupportedException("Cannot bind ARRAY types in dialect " + dialect); - } - } - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxx xxxxxxxxxxx x xxxxxxxxxxxxxxxx xxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - x - xx [/pro] */ - else if (EnumType.class.isAssignableFrom(type)) { - stmt.setString(nextIndex(), ((EnumType) value).getLiteral()); - } - else { - stmt.setObject(nextIndex(), value); - } - } + ((Field) field).getBinding().set( + new DefaultBindingSetStatementContext(configuration(), stmt, nextIndex(), value) + ); return this; } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java new file mode 100644 index 0000000000..814f8d3798 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -0,0 +1,2080 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import static java.lang.Boolean.TRUE; +import static java.lang.Integer.toOctalString; +import static java.util.Arrays.asList; +// ... +// ... +import static org.jooq.SQLDialect.CUBRID; +// ... +import static org.jooq.SQLDialect.DERBY; +import static org.jooq.SQLDialect.FIREBIRD; +import static org.jooq.SQLDialect.H2; +import static org.jooq.SQLDialect.HSQLDB; +// ... +// ... +import static org.jooq.SQLDialect.MARIADB; +import static org.jooq.SQLDialect.MYSQL; +// ... +import static org.jooq.SQLDialect.POSTGRES; +import static org.jooq.SQLDialect.SQLITE; +// ... +// ... +import static org.jooq.conf.ParamType.INLINED; +import static org.jooq.conf.ParamType.NAMED; +import static org.jooq.conf.ParamType.NAMED_OR_INLINED; +import static org.jooq.impl.DSL.name; +import static org.jooq.impl.DSL.using; +import static org.jooq.impl.DefaultExecuteContext.localTargetConnection; +import static org.jooq.impl.Utils.needsBackslashEscaping; +import static org.jooq.tools.StringUtils.leftPad; +import static org.jooq.tools.jdbc.JDBCUtils.safeFree; +import static org.jooq.tools.jdbc.JDBCUtils.wasNull; +import static org.jooq.tools.reflect.Reflect.on; +import static org.jooq.util.postgres.PostgresUtils.toPGArrayString; +import static org.jooq.util.postgres.PostgresUtils.toPGInterval; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.List; +import java.util.UUID; + +// ... +import org.jooq.Binding; +import org.jooq.BindingGetResultSetContext; +import org.jooq.BindingGetSQLInputContext; +import org.jooq.BindingGetStatementContext; +import org.jooq.BindingRegisterContext; +import org.jooq.BindingSQLContext; +import org.jooq.BindingSetSQLOutputContext; +import org.jooq.BindingSetStatementContext; +import org.jooq.Configuration; +import org.jooq.Context; +import org.jooq.Converter; +import org.jooq.DataType; +import org.jooq.EnumType; +import org.jooq.Field; +import org.jooq.RenderContext; +import org.jooq.Result; +import org.jooq.Row; +import org.jooq.SQLDialect; +import org.jooq.Schema; +import org.jooq.Scope; +import org.jooq.UDTRecord; +import org.jooq.exception.SQLDialectNotSupportedException; +import org.jooq.tools.Convert; +import org.jooq.tools.JooqLogger; +import org.jooq.tools.StringUtils; +import org.jooq.tools.jdbc.JDBCUtils; +import org.jooq.tools.jdbc.MockArray; +import org.jooq.types.DayToSecond; +import org.jooq.types.Interval; +import org.jooq.types.UByte; +import org.jooq.types.UInteger; +import org.jooq.types.ULong; +import org.jooq.types.UNumber; +import org.jooq.types.UShort; +import org.jooq.types.YearToMonth; +import org.jooq.util.postgres.PostgresUtils; + +/** + * @author Lukas Eder + */ +public class DefaultBinding implements Binding { + + static final JooqLogger log = JooqLogger.getLogger(DefaultBinding.class); + private static final char[] HEX = "0123456789abcdef".toCharArray(); + + /** + * Generated UID + */ + private static final long serialVersionUID = -198499389344950496L; + + final Class type; + final Converter converter; + + @Deprecated + // TODO: This type boolean should not be passed standalone to the + // constructor. Find a better design + final boolean isLob; + @Deprecated + final String paramName; + + public DefaultBinding(Converter converter) { + this(converter, false, null); + } + + DefaultBinding(Converter converter, boolean isLob) { + this(converter, isLob, null); + } + + DefaultBinding(Converter converter, boolean isLob, String paramName) { + this.type = converter.fromType(); + this.converter = converter; + this.isLob = isLob; + this.paramName = paramName; + } + + @Override + public void sql(BindingSQLContext ctx) { + T converted = converter.to(ctx.value()); + + // Casting can be enforced or prevented + switch (ctx.render().castMode()) { + case NEVER: + toSQL(ctx, converted); + return; + + case ALWAYS: + toSQLCast(ctx, converted); + return; + } + + // See if we "should" cast, to stay on the safe side + if (shouldCast(ctx, converted)) { + toSQLCast(ctx, converted); + } + + // Most RDBMS can infer types for bind values + else { + toSQL(ctx, converted); + } + } + + private final boolean shouldCast(BindingSQLContext ctx, T converted) { + + // In default mode, casting is only done when parameters are NOT inlined + if (ctx.render().paramType() != INLINED) { + + // Generated enums should not be cast... + if (!(converted instanceof EnumType)) { + switch (ctx.family()) { + + // These dialects can hardly detect the type of a bound constant. + /* [pro] xx + xxxx xxxx + xxxx xxxxxxxxx + xx [/pro] */ + case DERBY: + case FIREBIRD: + + // These dialects have some trouble, when they mostly get it right. + case H2: + case HSQLDB: + + // [#1261] There are only a few corner-cases, where this is + // really needed. Check back on related CUBRID bugs + case CUBRID: + + // [#1029] Postgres and [#632] Sybase need explicit casting + // in very rare cases. + /* [pro] xx + xxxx xxxxxxx + xx [/pro] */ + case POSTGRES: { + return true; + } + } + } + } + + // [#566] JDBC doesn't explicitly support interval data types. To be on + // the safe side, always cast these types in those dialects that support + // them + if (Interval.class.isAssignableFrom(type)) { + switch (ctx.family()) { + /* [pro] xx + xxxx xxxxxxx + xx [/pro] */ + case POSTGRES: + return true; + } + } + + return false; + } + + /** + * Render the bind variable including a cast, if necessary + */ + private final void toSQLCast(BindingSQLContext ctx, T converted) { + DataType dataType = DefaultDataType.getDataType(ctx.dialect(), type); + DataType sqlDataType = dataType.getSQLDataType(); + SQLDialect family = ctx.family(); + + // [#822] Some RDBMS need precision / scale information on BigDecimals + if (converted != null && type == BigDecimal.class && asList(CUBRID, DERBY, FIREBIRD, HSQLDB).contains(family)) { + + // Add precision / scale on BigDecimals + int scale = ((BigDecimal) converted).scale(); + int precision = scale + ((BigDecimal) converted).precision(); + + // Firebird's max precision is 18 + if (family == FIREBIRD) { + precision = Math.min(precision, 18); + } + + toSQLCast(ctx, converted, dataType, 0, precision, scale); + } + + // [#1028] Most databases don't know an OTHER type (except H2, HSQLDB). + else if (SQLDataType.OTHER == sqlDataType) { + + // If the bind value is set, it can be used to derive the cast type + if (converted != null) { + toSQLCast(ctx, converted, DefaultDataType.getDataType(family, converted.getClass()), 0, 0, 0); + } + + // [#632] [#722] Current integration tests show that Ingres and + // Sybase can do without casting in most cases. + else if (asList().contains(family)) { + ctx.render().sql(getBindVariable(ctx.render())); + } + + // Derby and DB2 must have a type associated with NULL. Use VARCHAR + // as a workaround. That's probably not correct in all cases, though + else { + toSQLCast(ctx, converted, DefaultDataType.getDataType(family, String.class), 0, 0, 0); + } + } + + // [#1029] Postgres generally doesn't need the casting. Only in the + // above case where the type is OTHER + // [#1125] Also with temporal data types, casting is needed some times + // [#1130] TODO type can be null for ARRAY types, etc. + else if (family == POSTGRES && (sqlDataType == null || !sqlDataType.isTemporal())) { + toSQL(ctx, converted); + } + + // [#1727] VARCHAR types should be cast to their actual lengths in some + // dialects + else if ((sqlDataType == SQLDataType.VARCHAR || sqlDataType == SQLDataType.CHAR) && asList(FIREBIRD).contains(family)) { + toSQLCast(ctx, converted, dataType, getValueLength(converted), 0, 0); + } + + /* [pro] xx + xx xxxxxxx xxxx xxxx xxxxx xxxxxx xxx xx xxxx xx xxx xxxxxx xx xxxx xxxxxxxx + xxxx xx xxxxxxxxxxxx xx xxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxx xx xx xxx + x + xx [/pro] */ + + // In all other cases, the bind variable can be cast normally + else { + toSQLCast(ctx, converted, dataType, dataType.length(), dataType.precision(), dataType.scale()); + } + } + + private final int getValueLength(T value) { + String string = (String) value; + if (string == null) { + return 1; + } + + else { + int length = string.length(); + + // If non 7-bit ASCII characters are present, multiply the length by + // 4 to be sure that even UTF-32 collations will fit. But don't use + // larger numbers than Derby's upper limit 32672 + for (int i = 0; i < length; i++) { + if (string.charAt(i) > 127) { + return Math.min(32672, 4 * length); + } + } + + return Math.min(32672, length); + } + } + + private final void toSQLCast(BindingSQLContext ctx, T converted, DataType dataType, int length, int precision, int scale) { + ctx.render().keyword("cast").sql("("); + toSQL(ctx, converted); + ctx.render().sql(" ").keyword("as").sql(" ") + .sql(dataType.length(length).precision(precision, scale).getCastTypeName(ctx.configuration())) + .sql(")"); + } + + /** + * Get a bind variable, depending on value of + * {@link RenderContext#namedParams()} + */ + private final String getBindVariable(RenderContext context) { + if (context.paramType() == NAMED || context.paramType() == NAMED_OR_INLINED) { + int index = context.nextIndex(); + + if (StringUtils.isBlank(paramName)) { + return ":" + index; + } + else { + return ":" + paramName; + } + } + else { + return "?"; + } + } + + /** + * Inlining abstraction + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private final void toSQL(BindingSQLContext ctx, Object val) { + SQLDialect family = ctx.family(); + RenderContext render = ctx.render(); + + if (render.paramType() == INLINED) { + // [#2223] Some type-casts in this section may seem unnecessary, e.g. + // ((Boolean) val).toString(). They have been put in place to avoid + // accidental type confusions where type != val.getClass(), and thus + // SQL injection may occur + + 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"); + } + /* [pro] xx + xxxx xx xxxxxxx xx xxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxx xxxx x xxxxx x xxxxxxx + x + xx [/pro] */ + else { + render.keyword(((Boolean) val).toString()); + } + } + + // [#1154] Binary data cannot always be inlined + else if (type == byte[].class) { + byte[] binary = (byte[]) val; + + if (asList().contains(family)) { + render.sql("0x") + .sql(convertBytesToHex(binary)); + } + /* [pro] xx + xxxx xx xxxxxxx xx xxxx x + xxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxx + x + xx [/pro] */ + 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(convertBytesToPostgresOctal(binary)) + .keyword("'::bytea"); + } + + // This default behaviour is used in debug logging for dialects + // that do not support inlining binary data + else { + render.sql("X'") + .sql(convertBytesToHex(binary)) + .sql("'"); + } + } + + // Interval extends Number, so let Interval come first! + else if (Interval.class.isAssignableFrom(type)) { + render.sql("'") + .sql(escape(val, render)) + .sql("'"); + } + + else if (Number.class.isAssignableFrom(type)) { + render.sql(((Number) val).toString()); + } + + // [#1156] Date/Time data types should be inlined using JDBC + // escape syntax + else if (type == Date.class) { + + // The SQLite JDBC driver does not implement the escape syntax + // [#1253] SQL Server and Sybase do not implement date literals + if (asList(SQLITE).contains(family)) { + render.sql("'").sql(escape(val, render)).sql("'"); + } + + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx + x + + xxxx xx xxxxxxx xx xxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xx xxxxxx + x + xx [/pro] */ + + // [#1253] Derby doesn't support the standard literal + else if (family == DERBY) { + render.keyword("date('").sql(escape(val, render)).sql("')"); + } + + // [#3648] Circumvent a MySQL bug related to date literals + else if (family == MYSQL) { + render.keyword("{d '").sql(escape(val, render)).sql("'}"); + } + + // Most dialects implement SQL standard date literals + else { + render.keyword("date '").sql(escape(val, render)).sql("'"); + } + } + else if (type == Timestamp.class) { + + // The SQLite JDBC driver does not implement the escape syntax + // [#1253] SQL Server and Sybase do not implement timestamp literals + if (asList(SQLITE).contains(family)) { + render.sql("'").sql(escape(val, render)).sql("'"); + } + + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx + x + + xxxx xx xxxxxxx xx xxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xx xxxxxxxxxxx + x + xx [/pro] */ + + // [#1253] Derby doesn't support the standard literal + else if (family == DERBY) { + render.keyword("timestamp('").sql(escape(val, render)).sql("')"); + } + + // CUBRID timestamps have no fractional seconds + else if (family == CUBRID) { + render.keyword("datetime '").sql(escape(val, render)).sql("'"); + } + + // [#3648] Circumvent a MySQL bug related to date literals + else if (family == MYSQL) { + render.keyword("{ts '").sql(escape(val, render)).sql("'}"); + } + + // Most dialects implement SQL standard timestamp literals + else { + render.keyword("timestamp '").sql(escape(val, render)).sql("'"); + } + } + else if (type == Time.class) { + + // The SQLite JDBC driver does not implement the escape syntax + // [#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) val)).sql("'"); + } + + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx + x + + xxxx xx xxxxxxx xx xxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xx xxxxxxxxx + x + xx [/pro] */ + + // [#1253] Derby doesn't support the standard literal + else if (family == DERBY) { + render.keyword("time").sql("('").sql(escape(val, render)).sql("')"); + } + + // [#3648] Circumvent a MySQL bug related to date literals + else if (family == MYSQL) { + render.keyword("{t '").sql(escape(val, render)).sql("'}"); + } + /* [pro] xx + xx xxxxxxx xxxxxx xxxxxxx xxxx xxxx xxxxxxxx + xxxx xx xxxxxxx xx xxxxxxx x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx + x + + xx [/pro] */ + // Most dialects implement SQL standard time literals + else { + render.keyword("time").sql(" '").sql(escape(val, 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(new IdentityConverter(type.getComponentType()), isLob).sql(new DefaultBindingSQLContext(ctx.configuration(), ctx.render(), o)); + separator = ", "; + } + + render.sql(")"); + } + + // By default, render HSQLDB / POSTGRES syntax + else { + render.keyword("ARRAY"); + render.sql("["); + + for (Object o : ((Object[]) val)) { + render.sql(separator); + new DefaultBinding(new IdentityConverter(type.getComponentType()), isLob).sql(new DefaultBindingSQLContext(ctx.configuration(), ctx.render(), o)); + separator = ", "; + } + + render.sql("]"); + + // [#3214] Some PostgreSQL array type literals need explicit casting + if (family == POSTGRES && EnumType.class.isAssignableFrom(type.getComponentType())) { + render.sql("::") + .keyword(DefaultDataType.getDataType(family, type).getCastTypeName(render.configuration())); + } + } + } + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxx + x + xx [/pro] */ + else if (EnumType.class.isAssignableFrom(type)) { + String literal = ((EnumType) val).getLiteral(); + + if (literal == null) { + new DefaultBinding(new IdentityConverter(String.class), isLob).sql(new DefaultBindingSQLContext(ctx.configuration(), ctx.render(), literal)); + } + else { + new DefaultBinding(new IdentityConverter(String.class), isLob).sql(new DefaultBindingSQLContext(ctx.configuration(), ctx.render(), literal)); + } + } + else if (UDTRecord.class.isAssignableFrom(type)) { + render.sql("[UDT]"); + } + + // Known fall-through types: + // - Blob, Clob (both not supported by jOOQ) + // - String + // - UUID + else { + render.sql("'") + .sql(escape(val, render), true) + .sql("'"); + } + } + + // In Postgres, some additional casting must be done in some cases... + else if (family == SQLDialect.POSTGRES) { + + // Postgres needs explicit casting for array types + if (type.isArray() && byte[].class != type) { + render.sql(getBindVariable(render)); + render.sql("::"); + render.keyword(DefaultDataType.getDataType(family, type).getCastTypeName(render.configuration())); + } + + // ... and also for enum types + else if (EnumType.class.isAssignableFrom(type)) { + render.sql(getBindVariable(render)); + + // [#968] Don't cast "synthetic" enum types (note, val can be null!) + EnumType e = (EnumType) type.getEnumConstants()[0]; + Schema schema = e.getSchema(); + + if (schema != null) { + render.sql("::"); + + schema = using(render.configuration()).map(schema); + if (schema != null && TRUE.equals(render.configuration().settings().isRenderSchema())) { + render.visit(schema); + render.sql("."); + } + + render.visit(name(e.getName())); + } + } + + else { + render.sql(getBindVariable(render)); + } + } + + else { + render.sql(getBindVariable(render)); + } + } + + /** + * Escape a string literal by replacing ' by '', and possibly also backslashes. + */ + private final String escape(Object val, Context context) { + String result = val.toString(); + + if (needsBackslashEscaping(context.configuration())) + result = result.replace("\\", "\\\\"); + + return result.replace("'", "''"); + } + + /** + * Convert a byte array to a hex encoded string. + * + * @param value the byte array + * @return the hex encoded string + */ + private static final String convertBytesToHex(byte[] value) { + return convertBytesToHex(value, value.length); + } + + /** + * Convert a byte array to a hex encoded string. + * + * @param value the byte array + * @param len the number of bytes to encode + * @return the hex encoded string + */ + private static final String convertBytesToHex(byte[] value, int len) { + char[] buff = new char[len + len]; + char[] hex = HEX; + for (int i = 0; i < len; i++) { + int c = value[i] & 0xff; + buff[i + i] = hex[c >> 4]; + buff[i + i + 1] = hex[c & 0xf]; + } + return new String(buff); + } + + /** + * Postgres uses octals instead of hex encoding + */ + private static final String convertBytesToPostgresOctal(byte[] binary) { + StringBuilder sb = new StringBuilder(); + + for (byte b : binary) { + sb.append("\\\\"); + sb.append(leftPad(toOctalString(b), 3, '0')); + } + + return sb.toString(); + } + + @Override + public void register(BindingRegisterContext ctx) throws SQLException { + Configuration configuration = ctx.configuration(); + int sqlType = DefaultDataType.getDataType(ctx.dialect(), type).getSQLType(); + + switch (configuration.dialect().family()) { + /* [pro] xx + + xx xxx xxxx xxxx xxxxxxx xxxxx xxxxxx xxxxx xx xxxx + xx xxxx xxx xxxx xxxx + xxxx xxxxxxx x + xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxx xxxxxx x xxxxx + xxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxx xxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxx + x + + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxxxx xxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxxxxxxxx + x + + xx xxx xxxxxxx xxxxxxxxx xx xxx xx xxxxxxxx x xxxx + xx xxxxxxx + xxxx x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx + x + + xxxxxx + x + + xx [/pro] */ + default: { + ctx.statement().registerOutParameter(ctx.index(), sqlType); + break; + } + } + } + + @Override + public void set(BindingSetStatementContext ctx) throws SQLException { + Configuration configuration = ctx.configuration(); + SQLDialect dialect = ctx.dialect(); + T value = converter.to(ctx.value()); + + if (log.isTraceEnabled()) { + if (value != null && value.getClass().isArray() && value.getClass() != byte[].class) { + log.trace("Binding variable " + ctx.index(), Arrays.asList((Object[]) value) + " (" + type + ")"); + } + else { + log.trace("Binding variable " + ctx.index(), value + " (" + type + ")"); + } + } + + // Setting null onto a prepared statement is subtly different for every + // SQL dialect. See the following section for details + if (value == null) { + int sqlType = DefaultDataType.getDataType(dialect, type).getSQLType(); + + /* [pro] xx + xx xxxxxxxxxxxx xxxxx xxxxx xxxx xx xx xxxxx xxxx xxxxx xxxx xxxx + xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxx xxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxxxxx + x + + xxxx + xx [/pro] */ + // [#1126] Oracle's UDTs need to be bound with their type name + if (UDTRecord.class.isAssignableFrom(type)) { + String typeName = Utils.newRecord(false, (Class>) type) + .operate(null) + .getUDT() + .getName(); + ctx.statement().setNull(ctx.index(), sqlType, typeName); + } + + // [#1225] [#1227] TODO Put this logic into DataType + // Some dialects have trouble binding binary data as BLOB + else if (asList(POSTGRES).contains(configuration.dialect()) && sqlType == Types.BLOB) { + ctx.statement().setNull(ctx.index(), Types.BINARY); + } + + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxx x + + xx xxxx xxxxxxxxxx xxxx xx xxxx xxxxxx xxxx xxx xxx xxxxxxxxx xxxxxx + xx xxxxxxxxxxx xxxxx xxxxxxx xxx xxxxxxx + xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxx xxxxxxxxx x + xxxx xxxxxxxxxxxxx + xxxx xxxxxxxxxxxxxxxx + xxxx xxxxxxxxxxxxxxxxxxxx + xxxx xxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx + xxxxxx + + xxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxx + xxxxxx + x + x + + xx xxxxxxx xxxxxxx xxx xxx xxxxxx xxxxxx xxxx xxx xxxx xxxxxxxx xxx xxx xxxx xxxxxx + xx xxxxxx xxx xxx xxxxxxx xxxxx + xxxx xx xxxxxxxx xx xxxxxxxxxxxxx xx xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxx + x + + xx [/pro] */ + // All other types can be set to null if the JDBC type is known + else if (sqlType != Types.OTHER) { + ctx.statement().setNull(ctx.index(), sqlType); + } + + /* [pro] xx + xx xxxxxx xxx xxx xxxxxxx xxxxxxx xxxxx xxxxxx xx xxx xx xxxx + xx xxxxxxxxxxx xxx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx + x + + xx xxxxxx xxx xxxxxxx xxxxxxx xxxxx xxx xx xxx xx xxxx xxxxx xxxxxxx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxx x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx + x + + xx [/pro] */ + // [#729] In the absence of the correct JDBC type, try setObject + else { + ctx.statement().setObject(ctx.index(), null); + } + } + else { + Class actualType = type; + + // Try to infer the bind value type from the actual bind value if possible. + if (actualType == Object.class) { + actualType = value.getClass(); + } + + if (actualType == Blob.class) { + ctx.statement().setBlob(ctx.index(), (Blob) value); + } + else if (actualType == Boolean.class) { + /* [pro] xx + xx xx xxxxxx xxxxxx xxxxxx xx xxxxx xxxxx xxxxx xx xxxxxxxxx xx xxxxxxxxxx xx xxxxxxx xxxxxxx + xx xxxxxxxxxxxxxxxxx xx xxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx xxxxx x x x xxx + xxxx + xx [/pro] */ + ctx.statement().setBoolean(ctx.index(), (Boolean) value); + } + else if (actualType == BigDecimal.class) { + if (asList(SQLITE).contains(dialect.family())) { + ctx.statement().setString(ctx.index(), value.toString()); + } + else { + ctx.statement().setBigDecimal(ctx.index(), (BigDecimal) value); + } + } + else if (actualType == BigInteger.class) { + if (asList(SQLITE).contains(dialect.family())) { + ctx.statement().setString(ctx.index(), value.toString()); + } + else { + ctx.statement().setBigDecimal(ctx.index(), new BigDecimal((BigInteger) value)); + } + } + else if (actualType == Byte.class) { + ctx.statement().setByte(ctx.index(), (Byte) value); + } + else if (actualType == byte[].class) { + ctx.statement().setBytes(ctx.index(), (byte[]) value); + } + else if (actualType == Clob.class) { + ctx.statement().setClob(ctx.index(), (Clob) value); + } + else if (actualType == Double.class) { + ctx.statement().setDouble(ctx.index(), (Double) value); + } + else if (actualType == Float.class) { + ctx.statement().setFloat(ctx.index(), (Float) value); + } + else if (actualType == Integer.class) { + ctx.statement().setInt(ctx.index(), (Integer) value); + } + else if (actualType == Long.class) { + /* [pro] xx + xx xxxxxxxxxxxxxxxxx xx xxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx + xxxx + xx [/pro] */ + ctx.statement().setLong(ctx.index(), (Long) value); + } + else if (actualType == Short.class) { + ctx.statement().setShort(ctx.index(), (Short) value); + } + else if (actualType == String.class) { + ctx.statement().setString(ctx.index(), (String) value); + } + + // There is potential for trouble when binding date time as such + // ------------------------------------------------------------- + else if (actualType == Date.class) { + if (dialect == SQLITE) { + ctx.statement().setString(ctx.index(), ((Date) value).toString()); + } + else { + ctx.statement().setDate(ctx.index(), (Date) value); + } + } + else if (actualType == Time.class) { + if (dialect == SQLITE) { + ctx.statement().setString(ctx.index(), ((Time) value).toString()); + } + else { + ctx.statement().setTime(ctx.index(), (Time) value); + } + } + else if (actualType == Timestamp.class) { + if (dialect == SQLITE) { + ctx.statement().setString(ctx.index(), ((Timestamp) value).toString()); + } + else { + ctx.statement().setTimestamp(ctx.index(), (Timestamp) value); + } + } + + // [#566] Interval data types are best bound as Strings + else if (actualType == YearToMonth.class) { + if (dialect == POSTGRES) { + ctx.statement().setObject(ctx.index(), toPGInterval((YearToMonth) value)); + } + else { + ctx.statement().setString(ctx.index(), value.toString()); + } + } + else if (actualType == DayToSecond.class) { + if (dialect == POSTGRES) { + ctx.statement().setObject(ctx.index(), toPGInterval((DayToSecond) value)); + } + else { + ctx.statement().setString(ctx.index(), value.toString()); + } + } + else if (actualType == UByte.class) { + ctx.statement().setShort(ctx.index(), ((UByte) value).shortValue()); + } + else if (actualType == UShort.class) { + ctx.statement().setInt(ctx.index(), ((UShort) value).intValue()); + } + else if (actualType == UInteger.class) { + /* [pro] xx + xx xxxxxxxxxxxxxxxxx xx xxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx + xxxx + xx [/pro] */ + ctx.statement().setLong(ctx.index(), ((UInteger) value).longValue()); + } + else if (actualType == ULong.class) { + /* [pro] xx + xx xxxxxxxxxxxxxxxxx xx xxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx + xxxx + xx [/pro] */ + ctx.statement().setBigDecimal(ctx.index(), new BigDecimal(value.toString())); + } + else if (actualType == UUID.class) { + switch (dialect.family()) { + + // [#1624] Some JDBC drivers natively support the + // java.util.UUID data type + case H2: + case POSTGRES: { + ctx.statement().setObject(ctx.index(), value); + break; + } + + /* [pro] xx + xx xxxxx xxx xxxxxxxx xxxx xxxx xxxxx xx xx xxxx xxxx xxxxxxxx + xx xxxx xx xxxx xxxxxxxxxx xxxxxxx xxxx xxxxxxxxxxxxxxxxxx + xxxx xxxxxxxxxx + xxxx xxxxxxx + + xx [/pro] */ + // Most databases don't have such a type. In this case, jOOQ + // simulates the type + default: { + ctx.statement().setString(ctx.index(), value.toString()); + break; + } + } + } + + // The type byte[] is handled earlier. byte[][] can be handled here + else if (actualType.isArray()) { + switch (dialect) { + case POSTGRES: { + ctx.statement().setString(ctx.index(), toPGArrayString((Object[]) value)); + break; + } + case HSQLDB: { + Object[] a = (Object[]) value; + Class t = actualType; + + // [#2325] Some array types are not natively supported by HSQLDB + // More integration tests are probably needed... + if (actualType == UUID[].class) { + a = Convert.convertArray(a, String[].class); + t = String[].class; + } + + ctx.statement().setArray(ctx.index(), new MockArray(dialect, a, t)); + break; + } + case H2: { + ctx.statement().setObject(ctx.index(), value); + break; + } + default: + throw new SQLDialectNotSupportedException("Cannot bind ARRAY types in dialect " + dialect); + } + } + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxxxx xxxxxxxxxxx x xxxxxxxxxxxxxxxx xxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + x + xx [/pro] */ + else if (EnumType.class.isAssignableFrom(actualType)) { + ctx.statement().setString(ctx.index(), ((EnumType) value).getLiteral()); + } + else { + ctx.statement().setObject(ctx.index(), value); + } + } + } + + @Override + public void set(BindingSetSQLOutputContext ctx) throws SQLException { + T value = converter.to(ctx.value()); + if (value == null) { + ctx.output().writeObject(null); + } + else if (type == Blob.class) { + ctx.output().writeBlob((Blob) value); + } + else if (type == Boolean.class) { + ctx.output().writeBoolean((Boolean) value); + } + else if (type == BigInteger.class) { + ctx.output().writeBigDecimal(new BigDecimal((BigInteger) value)); + } + else if (type == BigDecimal.class) { + ctx.output().writeBigDecimal((BigDecimal) value); + } + else if (type == Byte.class) { + ctx.output().writeByte((Byte) value); + } + else if (type == byte[].class) { + + // [#1327] Oracle cannot serialise BLOBs as byte[] to SQLOutput + // Use reflection to avoid dependency on OJDBC + if (isLob) { + Blob blob = null; + + try { + blob = on("oracle.sql.BLOB").call("createTemporary", + on(ctx.output()).call("getSTRUCT") + .call("getJavaSqlConnection").get(), + false, + on("oracle.sql.BLOB").get("DURATION_SESSION")).get(); + + blob.setBytes(1, (byte[]) value); + ctx.output().writeBlob(blob); + } + finally { + DefaultExecuteContext.register(blob); + } + } + else { + ctx.output().writeBytes((byte[]) value); + } + } + else if (type == Clob.class) { + ctx.output().writeClob((Clob) value); + } + else if (type == Date.class) { + ctx.output().writeDate((Date) value); + } + else if (type == Double.class) { + ctx.output().writeDouble((Double) value); + } + else if (type == Float.class) { + ctx.output().writeFloat((Float) value); + } + else if (type == Integer.class) { + ctx.output().writeInt((Integer) value); + } + else if (type == Long.class) { + ctx.output().writeLong((Long) value); + } + else if (type == Short.class) { + ctx.output().writeShort((Short) value); + } + else if (type == String.class) { + + // [#1327] Oracle cannot serialise CLOBs as String to SQLOutput + // Use reflection to avoid dependency on OJDBC + if (isLob) { + Clob clob = null; + + try { + clob = on("oracle.sql.CLOB").call("createTemporary", + on(ctx.output()).call("getSTRUCT") + .call("getJavaSqlConnection").get(), + false, + on("oracle.sql.CLOB").get("DURATION_SESSION")).get(); + + clob.setString(1, (String) value); + ctx.output().writeClob(clob); + } + finally { + DefaultExecuteContext.register(clob); + } + } + else { + ctx.output().writeString((String) value); + } + } + else if (type == Time.class) { + ctx.output().writeTime((Time) value); + } + else if (type == Timestamp.class) { + ctx.output().writeTimestamp((Timestamp) value); + } + else if (type == YearToMonth.class) { + ctx.output().writeString(value.toString()); + } + else if (type == DayToSecond.class) { + ctx.output().writeString(value.toString()); + } +// else if (type.isArray()) { +// stream.writeArray(value); +// } + else if (UNumber.class.isAssignableFrom(type)) { + ctx.output().writeString(value.toString()); + } + else if (type == UUID.class) { + ctx.output().writeString(value.toString()); + } + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + + xx xxxxxxx xx xxx xxxxxx xxxxxx xxxx xxxxxxxxxxxxxxxxxx xxx xxxx + xx xxx xx xxxxxxxxxxxxxxxxxxx xxxxx xx xxxxxxxxxxx xxxxxx xx xxxxxx + xxxxxxxxxxxxxx xxxxxxxxxxx x xxxxxxxxxxxxxxxx xxxxxx + xxxxxxxx xxxxx x xxxxxxxxxxxxxxxxxx + + xx xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxxxxxx x + xxxxxxxx xxxxxxxxx x xxx xxxxxxxxxxxxxxxxxxxxx + + xxx xxxx x x xx x x xxxxxxxxxxxxxxxxx xxxx + xxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + + xxxxx x xxxxxxxxxx + x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx + x + xx [/pro] */ + else if (EnumType.class.isAssignableFrom(type)) { + ctx.output().writeString(((EnumType) value).getLiteral()); + } + else if (UDTRecord.class.isAssignableFrom(type)) { + ctx.output().writeObject((UDTRecord) value); + } + else { + throw new UnsupportedOperationException("Type " + type + " is not supported"); + } + } + + @SuppressWarnings("unchecked") + @Override + public void get(BindingGetResultSetContext ctx) throws SQLException { + T result = null; + + if (type == Blob.class) { + result = (T) ctx.resultSet().getBlob(ctx.index()); + } + else if (type == Boolean.class) { + result = (T) wasNull(ctx.resultSet(), Boolean.valueOf(ctx.resultSet().getBoolean(ctx.index()))); + } + else if (type == BigInteger.class) { + // The SQLite JDBC driver doesn't support BigDecimals + if (ctx.configuration().dialect() == SQLDialect.SQLITE) { + result = Convert.convert(ctx.resultSet().getString(ctx.index()), (Class) BigInteger.class); + } + else { + BigDecimal b = ctx.resultSet().getBigDecimal(ctx.index()); + result = (T) (b == null ? null : b.toBigInteger()); + } + } + else if (type == BigDecimal.class) { + // The SQLite JDBC driver doesn't support BigDecimals + if (ctx.configuration().dialect() == SQLDialect.SQLITE) { + result = Convert.convert(ctx.resultSet().getString(ctx.index()), (Class) BigDecimal.class); + } + else { + result = (T) ctx.resultSet().getBigDecimal(ctx.index()); + } + } + else if (type == Byte.class) { + result = (T) wasNull(ctx.resultSet(), Byte.valueOf(ctx.resultSet().getByte(ctx.index()))); + } + else if (type == byte[].class) { + result = (T) ctx.resultSet().getBytes(ctx.index()); + } + else if (type == Clob.class) { + result = (T) ctx.resultSet().getClob(ctx.index()); + } + else if (type == Date.class) { + result = (T) getDate(ctx.configuration().dialect(), ctx.resultSet(), ctx.index()); + } + else if (type == Double.class) { + result = (T) wasNull(ctx.resultSet(), Double.valueOf(ctx.resultSet().getDouble(ctx.index()))); + } + else if (type == Float.class) { + result = (T) wasNull(ctx.resultSet(), Float.valueOf(ctx.resultSet().getFloat(ctx.index()))); + } + else if (type == Integer.class) { + result = (T) wasNull(ctx.resultSet(), Integer.valueOf(ctx.resultSet().getInt(ctx.index()))); + } + else if (type == Long.class) { + result = (T) wasNull(ctx.resultSet(), Long.valueOf(ctx.resultSet().getLong(ctx.index()))); + } + else if (type == Short.class) { + result = (T) wasNull(ctx.resultSet(), Short.valueOf(ctx.resultSet().getShort(ctx.index()))); + } + else if (type == String.class) { + result = (T) ctx.resultSet().getString(ctx.index()); + } + else if (type == Time.class) { + result = (T) getTime(ctx.configuration().dialect(), ctx.resultSet(), ctx.index()); + } + else if (type == Timestamp.class) { + result = (T) getTimestamp(ctx.configuration().dialect(), ctx.resultSet(), ctx.index()); + } + else if (type == YearToMonth.class) { + if (ctx.configuration().dialect() == POSTGRES) { + Object object = ctx.resultSet().getObject(ctx.index()); + result = (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); + } + else { + String string = ctx.resultSet().getString(ctx.index()); + result = (T) (string == null ? null : YearToMonth.valueOf(string)); + } + } + else if (type == DayToSecond.class) { + if (ctx.configuration().dialect() == POSTGRES) { + Object object = ctx.resultSet().getObject(ctx.index()); + result = (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); + } + else { + String string = ctx.resultSet().getString(ctx.index()); + result = (T) (string == null ? null : DayToSecond.valueOf(string)); + } + } + else if (type == UByte.class) { + result = (T) Convert.convert(ctx.resultSet().getString(ctx.index()), UByte.class); + } + else if (type == UShort.class) { + result = (T) Convert.convert(ctx.resultSet().getString(ctx.index()), UShort.class); + } + else if (type == UInteger.class) { + result = (T) Convert.convert(ctx.resultSet().getString(ctx.index()), UInteger.class); + } + else if (type == ULong.class) { + result = (T) Convert.convert(ctx.resultSet().getString(ctx.index()), ULong.class); + } + else if (type == UUID.class) { + switch (ctx.configuration().dialect().family()) { + + // [#1624] Some JDBC drivers natively support the + // java.util.UUID data type + case H2: + case POSTGRES: { + result = (T) ctx.resultSet().getObject(ctx.index()); + break; + } + + /* [pro] xx + xx xxxxx xxx xxxxxxxx xxxx xxxx xxxxx xx xx xxxx xxxx xxxxxxxx + xx xxxx xx xxxx xxxxxxxxxx xxxxxxx xxxx xxxxxxxxxxxxxxxxxx + xxxx xxxxxxxxxx + xxxx xxxxxxx + + xx [/pro] */ + // Most databases don't have such a type. In this case, jOOQ + // simulates the type + default: { + result = (T) Convert.convert(ctx.resultSet().getString(ctx.index()), UUID.class); + break; + } + } + } + + // The type byte[] is handled earlier. byte[][] can be handled here + else if (type.isArray()) { + switch (ctx.configuration().dialect()) { + case POSTGRES: { + result = pgGetArray(ctx, ctx.resultSet(), type, ctx.index()); + break; + } + + default: + // Note: due to a HSQLDB bug, it is not recommended to call rs.getObject() here: + // See https://sourceforge.net/tracker/?func=detail&aid=3181365&group_id=23316&atid=378131 + result = (T) convertArray(ctx.resultSet().getArray(ctx.index()), (Class) type); + break; + } + } + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxx x xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx + x + xx [/pro] */ + else if (EnumType.class.isAssignableFrom(type)) { + result = getEnumType(type, ctx.resultSet().getString(ctx.index())); + } + else if (UDTRecord.class.isAssignableFrom(type)) { + switch (ctx.configuration().dialect()) { + case POSTGRES: + result = (T) pgNewUDTRecord(type, ctx.resultSet().getObject(ctx.index())); + break; + + default: + result = (T) ctx.resultSet().getObject(ctx.index(), DataTypes.udtRecords()); + break; + } + + } + else if (Result.class.isAssignableFrom(type)) { + ResultSet nested = (ResultSet) ctx.resultSet().getObject(ctx.index()); + result = (T) DSL.using(ctx.configuration()).fetch(nested); + } + else { + result = (T) unlob(ctx.resultSet().getObject(ctx.index())); + } + + ctx.value(converter.from(result)); + } + + @SuppressWarnings("unchecked") + @Override + public void get(BindingGetStatementContext ctx) throws SQLException { + T result = null; + + if (type == Blob.class) { + result = (T) ctx.statement().getBlob(ctx.index()); + } + else if (type == Boolean.class) { + result = (T) wasNull(ctx.statement(), Boolean.valueOf(ctx.statement().getBoolean(ctx.index()))); + } + else if (type == BigInteger.class) { + BigDecimal d = ctx.statement().getBigDecimal(ctx.index()); + result = (T) (d == null ? null : d.toBigInteger()); + } + else if (type == BigDecimal.class) { + result = (T) ctx.statement().getBigDecimal(ctx.index()); + } + else if (type == Byte.class) { + result = (T) wasNull(ctx.statement(), Byte.valueOf(ctx.statement().getByte(ctx.index()))); + } + else if (type == byte[].class) { + result = (T) ctx.statement().getBytes(ctx.index()); + } + else if (type == Clob.class) { + result = (T) ctx.statement().getClob(ctx.index()); + } + else if (type == Date.class) { + result = (T) ctx.statement().getDate(ctx.index()); + } + else if (type == Double.class) { + result = (T) wasNull(ctx.statement(), Double.valueOf(ctx.statement().getDouble(ctx.index()))); + } + else if (type == Float.class) { + result = (T) wasNull(ctx.statement(), Float.valueOf(ctx.statement().getFloat(ctx.index()))); + } + else if (type == Integer.class) { + result = (T) wasNull(ctx.statement(), Integer.valueOf(ctx.statement().getInt(ctx.index()))); + } + else if (type == Long.class) { + result = (T) wasNull(ctx.statement(), Long.valueOf(ctx.statement().getLong(ctx.index()))); + } + else if (type == Short.class) { + result = (T) wasNull(ctx.statement(), Short.valueOf(ctx.statement().getShort(ctx.index()))); + } + else if (type == String.class) { + result = (T) ctx.statement().getString(ctx.index()); + } + else if (type == Time.class) { + result = (T) ctx.statement().getTime(ctx.index()); + } + else if (type == Timestamp.class) { + result = (T) ctx.statement().getTimestamp(ctx.index()); + } + else if (type == YearToMonth.class) { + if (ctx.configuration().dialect() == POSTGRES) { + Object object = ctx.statement().getObject(ctx.index()); + result = (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); + } + else { + String string = ctx.statement().getString(ctx.index()); + result = (T) (string == null ? null : YearToMonth.valueOf(string)); + } + } + else if (type == DayToSecond.class) { + if (ctx.configuration().dialect() == POSTGRES) { + Object object = ctx.statement().getObject(ctx.index()); + result = (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); + } + else { + String string = ctx.statement().getString(ctx.index()); + result = (T) (string == null ? null : DayToSecond.valueOf(string)); + } + } + else if (type == UByte.class) { + String string = ctx.statement().getString(ctx.index()); + result = (T) (string == null ? null : UByte.valueOf(string)); + } + else if (type == UShort.class) { + String string = ctx.statement().getString(ctx.index()); + result = (T) (string == null ? null : UShort.valueOf(string)); + } + else if (type == UInteger.class) { + String string = ctx.statement().getString(ctx.index()); + result = (T) (string == null ? null : UInteger.valueOf(string)); + } + else if (type == ULong.class) { + String string = ctx.statement().getString(ctx.index()); + result = (T) (string == null ? null : ULong.valueOf(string)); + } + else if (type == UUID.class) { + switch (ctx.configuration().dialect().family()) { + + // [#1624] Some JDBC drivers natively support the + // java.util.UUID data type + case H2: + case POSTGRES: { + result = (T) ctx.statement().getObject(ctx.index()); + break; + } + + /* [pro] xx + xx xxxxx xxx xxxxxxxx xxxx xxxx xxxxx xx xx xxxx xxxx xxxxxxxx + xx xxxx xx xxxx xxxxxxxxxx xxxxxxx xxxx xxxxxxxxxxxxxxxxxx + xxxx xxxxxxxxxx + xxxx xxxxxxx + + xx [/pro] */ + // Most databases don't have such a type. In this case, jOOQ + // simulates the type + default: { + result = (T) Convert.convert(ctx.statement().getString(ctx.index()), UUID.class); + break; + } + } + } + + // The type byte[] is handled earlier. byte[][] can be handled here + else if (type.isArray()) { + result = (T) convertArray(ctx.statement().getObject(ctx.index()), (Class)type); + } + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxx x xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx + x + xx [/pro] */ + else if (EnumType.class.isAssignableFrom(type)) { + result = getEnumType(type, ctx.statement().getString(ctx.index())); + } + else if (UDTRecord.class.isAssignableFrom(type)) { + switch (ctx.configuration().dialect()) { + case POSTGRES: + result = (T) pgNewUDTRecord(type, ctx.statement().getObject(ctx.index())); + break; + + default: + result = (T) ctx.statement().getObject(ctx.index(), DataTypes.udtRecords()); + break; + } + } + else if (Result.class.isAssignableFrom(type)) { + ResultSet nested = (ResultSet) ctx.statement().getObject(ctx.index()); + result = (T) DSL.using(ctx.configuration()).fetch(nested); + } + else { + result = (T) ctx.statement().getObject(ctx.index()); + } + + ctx.value(converter.from(result)); + } + + @SuppressWarnings("unchecked") + @Override + public void get(BindingGetSQLInputContext ctx) throws SQLException { + T result = null; + + if (type == Blob.class) { + result = (T) ctx.input().readBlob(); + } + else if (type == Boolean.class) { + result = (T) wasNull(ctx.input(), Boolean.valueOf(ctx.input().readBoolean())); + } + else if (type == BigInteger.class) { + BigDecimal d = ctx.input().readBigDecimal(); + result = (T) (d == null ? null : d.toBigInteger()); + } + else if (type == BigDecimal.class) { + result = (T) ctx.input().readBigDecimal(); + } + else if (type == Byte.class) { + result = (T) wasNull(ctx.input(), Byte.valueOf(ctx.input().readByte())); + } + else if (type == byte[].class) { + + // [#1327] Oracle cannot deserialise BLOBs as byte[] from SQLInput + if (isLob) { + Blob blob = null; + try { + blob = ctx.input().readBlob(); + result = (T) (blob == null ? null : blob.getBytes(1, (int) blob.length())); + } + finally { + safeFree(blob); + } + } + else { + result = (T) ctx.input().readBytes(); + } + } + else if (type == Clob.class) { + result = (T) ctx.input().readClob(); + } + else if (type == Date.class) { + result = (T) ctx.input().readDate(); + } + else if (type == Double.class) { + result = (T) wasNull(ctx.input(), Double.valueOf(ctx.input().readDouble())); + } + else if (type == Float.class) { + result = (T) wasNull(ctx.input(), Float.valueOf(ctx.input().readFloat())); + } + else if (type == Integer.class) { + result = (T) wasNull(ctx.input(), Integer.valueOf(ctx.input().readInt())); + } + else if (type == Long.class) { + result = (T) wasNull(ctx.input(), Long.valueOf(ctx.input().readLong())); + } + else if (type == Short.class) { + result = (T) wasNull(ctx.input(), Short.valueOf(ctx.input().readShort())); + } + else if (type == String.class) { + result = (T) ctx.input().readString(); + } + else if (type == Time.class) { + result = (T) ctx.input().readTime(); + } + else if (type == Timestamp.class) { + result = (T) ctx.input().readTimestamp(); + } + else if (type == YearToMonth.class) { + String string = ctx.input().readString(); + result = (T) (string == null ? null : YearToMonth.valueOf(string)); + } + else if (type == DayToSecond.class) { + String string = ctx.input().readString(); + result = (T) (string == null ? null : DayToSecond.valueOf(string)); + } + else if (type == UByte.class) { + String string = ctx.input().readString(); + result = (T) (string == null ? null : UByte.valueOf(string)); + } + else if (type == UShort.class) { + String string = ctx.input().readString(); + result = (T) (string == null ? null : UShort.valueOf(string)); + } + else if (type == UInteger.class) { + String string = ctx.input().readString(); + result = (T) (string == null ? null : UInteger.valueOf(string)); + } + else if (type == ULong.class) { + String string = ctx.input().readString(); + result = (T) (string == null ? null : ULong.valueOf(string)); + } + else if (type == UUID.class) { + result = (T) Convert.convert(ctx.input().readString(), UUID.class); + } + + // The type byte[] is handled earlier. byte[][] can be handled here + else if (type.isArray()) { + Array array = ctx.input().readArray(); + result = (T) (array == null ? null : array.getArray()); + } + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxx x xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx + x + xx [/pro] */ + else if (EnumType.class.isAssignableFrom(type)) { + result = getEnumType(type, ctx.input().readString()); + } + else if (UDTRecord.class.isAssignableFrom(type)) { + result = (T) ctx.input().readObject(); + } + else { + result = (T) unlob(ctx.input().readObject()); + } + + ctx.value(converter.from(result)); + } + + + + + /* [pro] xx + xxxxxxx xxxxxx xxxxx xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxx xxxxx xxxxxx xxxxxxx xxxxxxx xxxxxxxxxxxxxxx xxxxx xxxxxx xxxxxxxxxxxx x + xx xxxxxx xx xxxxx x + xxxxxx xxxxx + x + xxxx x + xx xxxxx xxxxxx xxx xxxxx xxxxxx xxxx xxxx xxxxxxx + xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxx + x + x + + xxxxxx xxxxx xxx xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxx xxxxxxx xxxxxx xxxxxxxxxxxx x + xx xxxxxxx xx xxxxx x + xxxxxxxxxxxxxxxx xxxxxx + x + xxxx x + xx xxxxxx xxxxx xxxxxx xxxx xx xxxxxx xx xxxx xxxxx xx xxxxxx + xx xxxxxx xxxxx xxxx xxxx xx xxxx xx xxxxxxx xxxx xx xxxxxx xx + xx xxxxxxxxx xxxxxxx xxx xxxxxx xxxxxxx + xxxxxxxx xxxxx x xxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxx xxxxxxxxx x xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxx + + xxx xxxx x x xx x x xxxxxxxxxxxxx xxxx + xxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + + xxxxxxxxxxxxxxxxxxxxxx + xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + x + + xxxxxx xxxxxxx + x + + xx [/pro] */ + /** + * [#2534] Extract byte[] or String data from a + * LOB, if the argument is a lob. + */ + private static Object unlob(Object object) throws SQLException { + if (object instanceof Blob) { + Blob blob = (Blob) object; + + try { + return blob.getBytes(1, (int) blob.length()); + } + finally { + JDBCUtils.safeFree(blob); + } + } + else if (object instanceof Clob) { + Clob clob = (Clob) object; + + try { + return clob.getSubString(1, (int) clob.length()); + } + finally { + JDBCUtils.safeFree(clob); + } + } + + return object; + } + + @SuppressWarnings("unchecked") + private static final T getEnumType(Class type, String literal) throws SQLException { + try { + Object[] list = (Object[]) type.getMethod("values").invoke(type); + + for (Object e : list) { + String l = ((EnumType) e).getLiteral(); + + if (l.equals(literal)) { + return (T) e; + } + } + } + catch (Exception e) { + throw new SQLException("Unknown enum literal found : " + literal); + } + + return null; + } + + private static final Object[] convertArray(Object array, Class type) throws SQLException { + if (array instanceof Object[]) { + return Convert.convert(array, type); + } + else if (array instanceof Array) { + return convertArray((Array) array, type); + } + + return null; + } + + private static final Object[] convertArray(Array array, Class type) throws SQLException { + if (array != null) { + return Convert.convert(array.getArray(), type); + } + + return null; + } + + private static final Date getDate(SQLDialect dialect, ResultSet rs, int index) throws SQLException { + + // SQLite's type affinity needs special care... + if (dialect == SQLDialect.SQLITE) { + String date = rs.getString(index); + + if (date != null) { + return new Date(parse("yyyy-MM-dd", date)); + } + + return null; + } + + // Cubrid SQL dates are incorrectly fetched. Reset milliseconds... + // See http://jira.cubrid.org/browse/APIS-159 + // See https://sourceforge.net/apps/trac/cubridinterface/ticket/140 + else if (dialect == CUBRID) { + Date date = rs.getDate(index); + + if (date != null) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(date.getTime()); + cal.set(Calendar.MILLISECOND, 0); + date = new Date(cal.getTimeInMillis()); + } + + return date; + } + + else { + return rs.getDate(index); + } + } + + private static final Time getTime(SQLDialect dialect, ResultSet rs, int index) throws SQLException { + + // SQLite's type affinity needs special care... + if (dialect == SQLDialect.SQLITE) { + String time = rs.getString(index); + + if (time != null) { + return new Time(parse("HH:mm:ss", time)); + } + + return null; + } + + // Cubrid SQL dates are incorrectly fetched. Reset milliseconds... + // See http://jira.cubrid.org/browse/APIS-159 + // See https://sourceforge.net/apps/trac/cubridinterface/ticket/140 + else if (dialect == CUBRID) { + Time time = rs.getTime(index); + + if (time != null) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time.getTime()); + cal.set(Calendar.MILLISECOND, 0); + time = new Time(cal.getTimeInMillis()); + } + + return time; + } + + else { + return rs.getTime(index); + } + } + + private static final Timestamp getTimestamp(SQLDialect dialect, ResultSet rs, int index) throws SQLException { + + // SQLite's type affinity needs special care... + if (dialect == SQLDialect.SQLITE) { + String timestamp = rs.getString(index); + + if (timestamp != null) { + return new Timestamp(parse("yyyy-MM-dd HH:mm:ss", timestamp)); + } + + return null; + } else { + return rs.getTimestamp(index); + } + } + + private static final long parse(String pattern, String date) throws SQLException { + try { + + // Try reading a plain number first + try { + return Long.valueOf(date); + } + + // If that fails, try reading a formatted date + catch (NumberFormatException e) { + return new SimpleDateFormat(pattern).parse(date).getTime(); + } + } + catch (ParseException e) { + throw new SQLException("Could not parse date " + date, e); + } + } + + // ------------------------------------------------------------------------- + // XXX: The following section has been added for Postgres UDT support. The + // official Postgres JDBC driver does not implement SQLData and similar + // interfaces. Instead, a string representation of a UDT has to be parsed + // ------------------------------------------------------------------------- + + @SuppressWarnings("unchecked") + private static final T pgFromString(Class type, String string) throws SQLException { + if (string == null) { + return null; + } + else if (type == Blob.class) { + // Not supported + } + else if (type == Boolean.class) { + return (T) Boolean.valueOf(string); + } + else if (type == BigInteger.class) { + return (T) new BigInteger(string); + } + else if (type == BigDecimal.class) { + return (T) new BigDecimal(string); + } + else if (type == Byte.class) { + return (T) Byte.valueOf(string); + } + else if (type == byte[].class) { + return (T) PostgresUtils.toBytes(string); + } + else if (type == Clob.class) { + // Not supported + } + else if (type == Date.class) { + SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd"); + return (T) new Date(pgParseDate(string, f).getTime()); + } + else if (type == Double.class) { + return (T) Double.valueOf(string); + } + else if (type == Float.class) { + return (T) Float.valueOf(string); + } + else if (type == Integer.class) { + return (T) Integer.valueOf(string); + } + else if (type == Long.class) { + return (T) Long.valueOf(string); + } + else if (type == Short.class) { + return (T) Short.valueOf(string); + } + else if (type == String.class) { + return (T) string; + } + else if (type == Time.class) { + SimpleDateFormat f = new SimpleDateFormat("HH:mm:ss"); + return (T) new Time(pgParseDate(string, f).getTime()); + } + else if (type == Timestamp.class) { + SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return (T) new Timestamp(pgParseDate(string, f).getTime()); + } + else if (type == UByte.class) { + return (T) UByte.valueOf(string); + } + else if (type == UShort.class) { + return (T) UShort.valueOf(string); + } + else if (type == UInteger.class) { + return (T) UInteger.valueOf(string); + } + else if (type == ULong.class) { + return (T) ULong.valueOf(string); + } + else if (type == UUID.class) { + return (T) UUID.fromString(string); + } + else if (type.isArray()) { + return (T) pgNewArray(type, string); + } + /* [pro] xx + xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xx xxx xxxxxxxxx + x + xx [/pro] */ + else if (EnumType.class.isAssignableFrom(type)) { + return getEnumType(type, string); + } + else if (UDTRecord.class.isAssignableFrom(type)) { + return (T) pgNewUDTRecord(type, string); + } + + throw new UnsupportedOperationException("Class " + type + " is not supported"); + } + + private static final java.util.Date pgParseDate(String string, SimpleDateFormat f) throws SQLException { + try { + return f.parse(string); + } + catch (ParseException e) { + throw new SQLException(e); + } + } + + /** + * Create a UDT record from a PGobject + *

+ * Unfortunately, this feature is very poorly documented and true UDT + * support by the PostGreSQL JDBC driver has been postponed for a long time. + * + * @param object An object of type PGobject. The actual argument type cannot + * be expressed in the method signature, as no explicit + * dependency to postgres logic is desired + * @return The converted {@link UDTRecord} + */ + @SuppressWarnings("unchecked") + private static final UDTRecord pgNewUDTRecord(Class type, final Object object) throws SQLException { + if (object == null) { + return null; + } + + return Utils.newRecord(true, (Class>) type) + .operate(new RecordOperation, SQLException>() { + + @Override + public UDTRecord operate(UDTRecord record) throws SQLException { + List values = PostgresUtils.toPGObject(object.toString()); + + Row row = record.fieldsRow(); + for (int i = 0; i < row.size(); i++) { + pgSetValue(record, row.field(i), values.get(i)); + } + + return record; + } + }); + } + + /** + * Workarounds for the unimplemented Postgres JDBC driver features + */ + @SuppressWarnings("unchecked") + private static final T pgGetArray(Scope ctx, ResultSet rs, Class type, int index) throws SQLException { + + // Get the JDBC Array and check for null. If null, that's OK + Array array = rs.getArray(index); + if (array == null) { + return null; + } + + // Try fetching a Java Object[]. That's gonna work for non-UDT types + try { + return (T) convertArray(rs.getArray(index), (Class) type); + } + + // This might be a UDT (not implemented exception...) + catch (Exception e) { + List result = new ArrayList(); + + // Try fetching the array as a JDBC ResultSet + try { + while (rs.next()) { + DefaultBindingGetResultSetContext out = new DefaultBindingGetResultSetContext(ctx.configuration(), rs, 2); + new DefaultBinding(new IdentityConverter((Class) type.getComponentType()), false).get(out); + result.add(out.value()); + } + } + + // That might fail too, then we don't know any further... + catch (Exception fatal) { + log.error("Cannot parse Postgres array: " + rs.getString(index)); + log.error(fatal); + return null; + } + + return (T) convertArray(result.toArray(), (Class) type); + } + } + + /** + * Create an array from a String + *

+ * Unfortunately, this feature is very poorly documented and true UDT + * support by the PostGreSQL JDBC driver has been postponed for a long time. + * + * @param string A String representation of an array + * @return The converted array + */ + private static final Object[] pgNewArray(Class type, String string) throws SQLException { + if (string == null) { + return null; + } + + try { + Class component = type.getComponentType(); + String values = string.replaceAll("^\\{(.*)\\}$", "$1"); + + if ("".equals(values)) { + return (Object[]) java.lang.reflect.Array.newInstance(component, 0); + } + else { + String[] split = values.split(","); + Object[] result = (Object[]) java.lang.reflect.Array.newInstance(component, split.length); + + for (int i = 0; i < split.length; i++) { + result[i] = pgFromString(type.getComponentType(), split[i]); + } + + return result; + } + } + catch (Exception e) { + throw new SQLException(e); + } + } + + private static final void pgSetValue(UDTRecord record, Field field, String value) throws SQLException { + record.setValue(field, pgFromString(field.getType(), value)); + } +} + diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingContext.java new file mode 100644 index 0000000000..8bc00f0437 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingContext.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import org.jooq.Configuration; + +/** + * @author Lukas Eder + */ +@Deprecated +class DefaultBindingContext extends AbstractBindingContext { + DefaultBindingContext(Configuration configuration) { + super(configuration); + } +} \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetResultSetContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetResultSetContext.java new file mode 100644 index 0000000000..5bfeffa9e8 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetResultSetContext.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import java.sql.ResultSet; + +import org.jooq.BindingGetResultSetContext; +import org.jooq.Configuration; + +/** + * @author Lukas Eder + */ +class DefaultBindingGetResultSetContext extends AbstractScope implements BindingGetResultSetContext { + + private final ResultSet resultSet; + private final int index; + private T value; + + DefaultBindingGetResultSetContext(Configuration configuration, ResultSet resultSet, int index) { + super(configuration); + + this.resultSet = resultSet; + this.index = index; + } + + @Override + public final ResultSet resultSet() { + return resultSet; + } + + @Override + public final int index() { + return index; + } + + @Override + public final void value(T v) { + this.value = v; + } + + final T value() { + return value; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetSQLInputContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetSQLInputContext.java new file mode 100644 index 0000000000..e9aeceda17 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetSQLInputContext.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import java.sql.SQLInput; + +import org.jooq.BindingGetSQLInputContext; +import org.jooq.Configuration; + +/** + * @author Lukas Eder + */ +class DefaultBindingGetSQLInputContext extends AbstractScope implements BindingGetSQLInputContext { + + private final SQLInput input; + private T value; + + DefaultBindingGetSQLInputContext(Configuration configuration, SQLInput input) { + super(configuration); + + this.input = input; + } + + @Override + public final SQLInput input() { + return input; + } + + @Override + public final void value(T v) { + this.value = v; + } + + final T value() { + return value; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetStatementContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetStatementContext.java new file mode 100644 index 0000000000..9b0cf404a6 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingGetStatementContext.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import java.sql.CallableStatement; + +import org.jooq.BindingGetStatementContext; +import org.jooq.Configuration; + +/** + * @author Lukas Eder + */ +class DefaultBindingGetStatementContext extends AbstractScope implements BindingGetStatementContext { + + private final CallableStatement statement; + private final int index; + private T value; + + DefaultBindingGetStatementContext(Configuration configuration, CallableStatement statement, int index) { + super(configuration); + + this.statement = statement; + this.index = index; + } + + @Override + public final CallableStatement statement() { + return statement; + } + + @Override + public final int index() { + return index; + } + + @Override + public final void value(T v) { + this.value = v; + } + + final T value() { + return value; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingRegisterContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingRegisterContext.java new file mode 100644 index 0000000000..4d4bc6fa8d --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingRegisterContext.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import java.sql.CallableStatement; + +import org.jooq.BindingRegisterContext; +import org.jooq.Configuration; + +/** + * @author Lukas Eder + */ +class DefaultBindingRegisterContext extends AbstractScope implements BindingRegisterContext { + + private final CallableStatement statement; + private final int index; + + DefaultBindingRegisterContext(Configuration configuration, CallableStatement statement, int index) { + super(configuration); + + this.statement = statement; + this.index = index; + } + + @Override + public final CallableStatement statement() { + return statement; + } + + @Override + public final int index() { + return index; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSQLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSQLContext.java new file mode 100644 index 0000000000..85e796c80e --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSQLContext.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import org.jooq.BindingSQLContext; +import org.jooq.Configuration; +import org.jooq.RenderContext; + +/** + * @author Lukas Eder + */ +class DefaultBindingSQLContext extends AbstractScope implements BindingSQLContext { + + private final RenderContext render; + private final T value; + + DefaultBindingSQLContext(Configuration configuration, RenderContext render, T value) { + super(configuration); + + this.render = render; + this.value = value; + } + + @Override + public final RenderContext render() { + return render; + } + + @Override + public final T value() { + return value; + } +} \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetSQLOutputContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetSQLOutputContext.java new file mode 100644 index 0000000000..231133dcb8 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetSQLOutputContext.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import java.sql.SQLOutput; + +import org.jooq.BindingSetSQLOutputContext; +import org.jooq.Configuration; + +/** + * @author Lukas Eder + */ +class DefaultBindingSetSQLOutputContext extends AbstractScope implements BindingSetSQLOutputContext { + + private final SQLOutput output; + private final T value; + + DefaultBindingSetSQLOutputContext(Configuration configuration, SQLOutput output, T value) { + super(configuration); + + this.output = output; + this.value = value; + } + + @Override + public final SQLOutput output() { + return output; + } + + @Override + public final T value() { + return value; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetStatementContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetStatementContext.java new file mode 100644 index 0000000000..b1b88d8aae --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindingSetStatementContext.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import java.sql.PreparedStatement; + +import org.jooq.BindingSetStatementContext; +import org.jooq.Configuration; + +/** + * @author Lukas Eder + */ +class DefaultBindingSetStatementContext extends AbstractScope implements BindingSetStatementContext { + + private final PreparedStatement statement; + private final int index; + private final T value; + + DefaultBindingSetStatementContext(Configuration configuration, PreparedStatement statement, int index, T value) { + super(configuration); + + this.statement = statement; + this.index = index; + this.value = value; + } + + @Override + public final PreparedStatement statement() { + return statement; + } + + @Override + public final int index() { + return index; + } + + @Override + public final T value() { + return value; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java index ff565c9c25..45e2e904ee 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java @@ -220,7 +220,7 @@ import org.jooq.tools.reflect.ReflectException; * @author Lukas Eder */ @SuppressWarnings({ "rawtypes", "unchecked" }) -public class DefaultDSLContext implements DSLContext, Serializable { +public class DefaultDSLContext extends AbstractScope implements DSLContext, Serializable { /** * Generated UID @@ -228,8 +228,6 @@ public class DefaultDSLContext implements DSLContext, Serializable { private static final long serialVersionUID = 2681360188806309513L; private static final JooqLogger log = JooqLogger.getLogger(DefaultDSLContext.class); - private final Configuration configuration; - // ------------------------------------------------------------------------- // XXX Constructors // ------------------------------------------------------------------------- @@ -267,48 +265,21 @@ public class DefaultDSLContext implements DSLContext, Serializable { } public DefaultDSLContext(Configuration configuration) { - - // The Configuration can be null when unattached Query objects are - // executed or when unattached Records are stored... - if (configuration == null) { - configuration = new DefaultConfiguration(); - } - - this.configuration = configuration; + super(configuration); } // ------------------------------------------------------------------------- // XXX Configuration API // ------------------------------------------------------------------------- - @Override - public Configuration configuration() { - return configuration; - } - - @Override - public Settings settings() { - return Utils.settings(configuration()); - } - - @Override - public SQLDialect dialect() { - return Utils.configuration(configuration()).dialect(); - } - - @Override - public SQLDialect family() { - return dialect().family(); - } - @Override public Schema map(Schema schema) { - return Utils.getMappedSchema(configuration, schema); + return Utils.getMappedSchema(configuration(), schema); } @Override public Table map(Table table) { - return Utils.getMappedTable(configuration, table); + return Utils.getMappedTable(configuration(), table); } // ------------------------------------------------------------------------- @@ -317,7 +288,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Meta meta() { - return new MetaImpl(configuration); + return new MetaImpl(configuration()); } // ------------------------------------------------------------------------- @@ -328,7 +299,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { public T transactionResult(TransactionalCallable transactional) { T result = null; - DefaultTransactionContext ctx = new DefaultTransactionContext(configuration.derive()); + DefaultTransactionContext ctx = new DefaultTransactionContext(configuration().derive()); TransactionProvider provider = ctx.configuration().transactionProvider(); try { @@ -379,7 +350,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public RenderContext renderContext() { - return new DefaultRenderContext(configuration); + return new DefaultRenderContext(configuration()); } @Override @@ -419,7 +390,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { } final Map> extractParams0(QueryPart part, boolean includeInlinedParams) { - ParamCollector collector = new ParamCollector(configuration, includeInlinedParams); + ParamCollector collector = new ParamCollector(configuration(), includeInlinedParams); collector.visit(part); return Collections.unmodifiableMap(collector.result); } @@ -431,7 +402,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public BindContext bindContext(PreparedStatement stmt) { - return new DefaultBindContext(configuration, stmt); + return new DefaultBindContext(configuration(), stmt); } @Override @@ -452,7 +423,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public void attach(Collection attachables) { for (Attachable attachable : attachables) { - attachable.attach(configuration); + attachable.attach(configuration()); } } @@ -462,7 +433,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public > LoaderOptionsStep loadInto(Table table) { - return new LoaderImpl(configuration, table); + return new LoaderImpl(configuration(), table); } // ------------------------------------------------------------------------- @@ -486,7 +457,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @SuppressWarnings("deprecation") Query query(org.jooq.Template template, Object... parameters) { - return new SQLQuery(configuration, queryPart(template, parameters)); + return new SQLQuery(configuration(), queryPart(template, parameters)); } @Override @@ -636,7 +607,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @SuppressWarnings("deprecation") ResultQuery resultQuery(org.jooq.Template template, Object... parameters) { - return new SQLResultQuery(configuration, queryPart(template, parameters)); + return new SQLResultQuery(configuration(), queryPart(template, parameters)); } // ------------------------------------------------------------------------- @@ -726,7 +697,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Cursor fetchLazy(ResultSet rs) { try { - return fetchLazy(rs, new MetaDataFieldProvider(configuration, rs.getMetaData()).getFields()); + return fetchLazy(rs, new MetaDataFieldProvider(configuration(), rs.getMetaData()).getFields()); } catch (SQLException e) { throw new DataAccessException("Error while accessing ResultSet meta data", e); @@ -735,7 +706,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Cursor fetchLazy(ResultSet rs, Field... fields) { - ExecuteContext ctx = new DefaultExecuteContext(configuration); + ExecuteContext ctx = new DefaultExecuteContext(configuration()); ExecuteListener listener = new ExecuteListeners(ctx); ctx.resultSet(rs); @@ -835,7 +806,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Result fetchFromStringData(List data) { if (data.size() == 0) { - return new ResultImpl(configuration); + return new ResultImpl(configuration()); } else { List> fields = new ArrayList>(); @@ -844,7 +815,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { fields.add(fieldByName(String.class, name)); } - Result result = new ResultImpl(configuration, fields); + Result result = new ResultImpl(configuration(), fields); if (data.size() > 1) { for (String[] values : data.subList(1, data.size())) { @@ -869,52 +840,52 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public WithAsStep with(String alias) { - return new WithImpl(configuration, false).with(alias); + return new WithImpl(configuration(), false).with(alias); } @Override public WithAsStep with(String alias, String... fieldAliases) { - return new WithImpl(configuration, false).with(alias, fieldAliases); + return new WithImpl(configuration(), false).with(alias, fieldAliases); } @Override public WithStep with(CommonTableExpression... tables) { - return new WithImpl(configuration, false).with(tables); + return new WithImpl(configuration(), false).with(tables); } @Override public WithAsStep withRecursive(String alias) { - return new WithImpl(configuration, true).with(alias); + return new WithImpl(configuration(), true).with(alias); } @Override public WithAsStep withRecursive(String alias, String... fieldAliases) { - return new WithImpl(configuration, true).with(alias, fieldAliases); + return new WithImpl(configuration(), true).with(alias, fieldAliases); } @Override public WithStep withRecursive(CommonTableExpression... tables) { - return new WithImpl(configuration, true).with(tables); + return new WithImpl(configuration(), true).with(tables); } @Override public SelectWhereStep selectFrom(Table table) { SelectWhereStep result = DSL.selectFrom(table); - result.attach(configuration); + result.attach(configuration()); return result; } @Override public SelectSelectStep select(Collection> fields) { SelectSelectStep result = DSL.select(fields); - result.attach(configuration); + result.attach(configuration()); return result; } @Override public SelectSelectStep select(Field... fields) { SelectSelectStep result = DSL.select(fields); - result.attach(configuration); + result.attach(configuration()); return result; } @@ -1057,14 +1028,14 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public SelectSelectStep selectDistinct(Collection> fields) { SelectSelectStep result = DSL.selectDistinct(fields); - result.attach(configuration); + result.attach(configuration()); return result; } @Override public SelectSelectStep selectDistinct(Field... fields) { SelectSelectStep result = DSL.selectDistinct(fields); - result.attach(configuration); + result.attach(configuration()); return result; } @@ -1207,42 +1178,42 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public SelectSelectStep> selectZero() { SelectSelectStep> result = DSL.selectZero(); - result.attach(configuration); + result.attach(configuration()); return result; } @Override public SelectSelectStep> selectOne() { SelectSelectStep> result = DSL.selectOne(); - result.attach(configuration); + result.attach(configuration()); return result; } @Override public SelectSelectStep> selectCount() { SelectSelectStep> result = DSL.selectCount(); - result.attach(configuration); + result.attach(configuration()); return result; } @Override public SelectQuery selectQuery() { - return new SelectQueryImpl(null, configuration); + return new SelectQueryImpl(null, configuration()); } @Override public SelectQuery selectQuery(TableLike table) { - return new SelectQueryImpl(null, configuration, table); + return new SelectQueryImpl(null, configuration(), table); } @Override public InsertQuery insertQuery(Table into) { - return new InsertQueryImpl(configuration, into); + return new InsertQueryImpl(configuration(), into); } @Override public InsertSetStep insertInto(Table into) { - return new InsertImpl(configuration, into, Collections.>emptyList()); + return new InsertImpl(configuration(), into, Collections.>emptyList()); } // [jooq-tools] START [insert] @@ -1250,160 +1221,160 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep1 insertInto(Table into, Field field1) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep2 insertInto(Table into, Field field1, Field field2) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep3 insertInto(Table into, Field field1, Field field2, Field field3) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep4 insertInto(Table into, Field field1, Field field2, Field field3, Field field4) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep5 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep6 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep7 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep8 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep9 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep10 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep11 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep12 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep13 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep14 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep15 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep16 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep17 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep18 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep19 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18, Field field19) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep20 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18, Field field19, Field field20) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep21 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18, Field field19, Field field20, Field field21) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21 })); } @Generated("This method was generated using jOOQ-tools") @Override public InsertValuesStep22 insertInto(Table into, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18, Field field19, Field field20, Field field21, Field field22) { - return new InsertImpl(configuration, into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21, field22 })); + return new InsertImpl(configuration(), into, Arrays.asList(new Field[] { field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21, field22 })); } // [jooq-tools] END [insert] @Override public InsertValuesStepN insertInto(Table into, Field... fields) { - return new InsertImpl(configuration, into, Arrays.asList(fields)); + return new InsertImpl(configuration(), into, Arrays.asList(fields)); } @Override public InsertValuesStepN insertInto(Table into, Collection> fields) { - return new InsertImpl(configuration, into, fields); + return new InsertImpl(configuration(), into, fields); } @Override public UpdateQuery updateQuery(Table table) { - return new UpdateQueryImpl(configuration, table); + return new UpdateQueryImpl(configuration(), table); } @Override public UpdateSetFirstStep update(Table table) { - return new UpdateImpl(configuration, table); + return new UpdateImpl(configuration(), table); } @Override public MergeUsingStep mergeInto(Table table) { - return new MergeImpl(configuration, table); + return new MergeImpl(configuration(), table); } // [jooq-tools] START [merge] @@ -1411,133 +1382,133 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep1 mergeInto(Table table, Field field1) { - return new MergeImpl(configuration, table, Arrays.asList(field1)); + return new MergeImpl(configuration(), table, Arrays.asList(field1)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep2 mergeInto(Table table, Field field1, Field field2) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep3 mergeInto(Table table, Field field1, Field field2, Field field3) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep4 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep5 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep6 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep7 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep8 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep9 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep10 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep11 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep12 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep13 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep14 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep15 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep16 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep17 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep18 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep19 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18, Field field19) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep20 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18, Field field19, Field field20) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep21 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18, Field field19, Field field20, Field field21) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21)); } @Generated("This method was generated using jOOQ-tools") @Override public MergeKeyStep22 mergeInto(Table table, Field field1, Field field2, Field field3, Field field4, Field field5, Field field6, Field field7, Field field8, Field field9, Field field10, Field field11, Field field12, Field field13, Field field14, Field field15, Field field16, Field field17, Field field18, Field field19, Field field20, Field field21, Field field22) { - return new MergeImpl(configuration, table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21, field22)); + return new MergeImpl(configuration(), table, Arrays.asList(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21, field22)); } // [jooq-tools] END [merge] @@ -1549,17 +1520,17 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public MergeKeyStepN mergeInto(Table table, Collection> fields) { - return new MergeImpl(configuration, table, fields); + return new MergeImpl(configuration(), table, fields); } @Override public DeleteQuery deleteQuery(Table table) { - return new DeleteQueryImpl(configuration, table); + return new DeleteQueryImpl(configuration(), table); } @Override public DeleteWhereStep delete(Table table) { - return new DeleteImpl(configuration, table); + return new DeleteImpl(configuration(), table); } // ------------------------------------------------------------------------- @@ -1568,7 +1539,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Batch batch(Query... queries) { - return new BatchMultiple(configuration, queries); + return new BatchMultiple(configuration(), queries); } @Override @@ -1589,7 +1560,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public BatchBindStep batch(Query query) { - return new BatchSingle(configuration, query); + return new BatchSingle(configuration(), query); } @Override @@ -1609,7 +1580,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Batch batchStore(UpdatableRecord... records) { - return new BatchCRUD(configuration, Action.STORE, records); + return new BatchCRUD(configuration(), Action.STORE, records); } @Override @@ -1619,7 +1590,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Batch batchInsert(TableRecord... records) { - return new BatchCRUD(configuration, Action.INSERT, records); + return new BatchCRUD(configuration(), Action.INSERT, records); } @Override @@ -1629,7 +1600,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Batch batchUpdate(UpdatableRecord... records) { - return new BatchCRUD(configuration, Action.UPDATE, records); + return new BatchCRUD(configuration(), Action.UPDATE, records); } @Override @@ -1639,7 +1610,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Batch batchDelete(UpdatableRecord... records) { - return new BatchCRUD(configuration, Action.DELETE, records); + return new BatchCRUD(configuration(), Action.DELETE, records); } @Override @@ -1658,7 +1629,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public CreateViewAsStep createView(Table view, Field... fields) { - return new CreateViewImpl(configuration, view, fields); + return new CreateViewImpl(configuration(), view, fields); } @Override @@ -1668,17 +1639,17 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public CreateTableAsStep createTable(Table table) { - return new CreateTableImpl(configuration, table); + return new CreateTableImpl(configuration(), table); } @Override public CreateIndexStep createIndex(String index) { - return new CreateIndexImpl(configuration, index); + return new CreateIndexImpl(configuration(), index); } @Override public CreateSequenceFinalStep createSequence(Sequence sequence) { - return new CreateSequenceImpl(configuration, sequence); + return new CreateSequenceImpl(configuration(), sequence); } @Override @@ -1688,7 +1659,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public AlterSequenceRestartStep alterSequence(Sequence sequence) { - return new AlterSequenceImpl(configuration, sequence); + return new AlterSequenceImpl(configuration(), sequence); } @Override @@ -1698,7 +1669,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public AlterTableStep alterTable(Table table) { - return new AlterTableImpl(configuration, table); + return new AlterTableImpl(configuration(), table); } @Override @@ -1708,7 +1679,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public DropViewFinalStep dropView(Table table) { - return new DropViewImpl(configuration, table); + return new DropViewImpl(configuration(), table); } @Override @@ -1718,7 +1689,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public DropViewFinalStep dropViewIfExists(Table table) { - return new DropViewImpl(configuration, table, true); + return new DropViewImpl(configuration(), table, true); } @Override @@ -1728,7 +1699,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public DropTableStep dropTable(Table table) { - return new DropTableImpl(configuration, table); + return new DropTableImpl(configuration(), table); } @Override @@ -1738,7 +1709,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public DropTableStep dropTableIfExists(Table table) { - return new DropTableImpl(configuration, table, true); + return new DropTableImpl(configuration(), table, true); } @Override @@ -1748,17 +1719,17 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public DropIndexFinalStep dropIndex(String index) { - return new DropIndexImpl(configuration, index); + return new DropIndexImpl(configuration(), index); } @Override public DropIndexFinalStep dropIndexIfExists(String index) { - return new DropIndexImpl(configuration, index, true); + return new DropIndexImpl(configuration(), index, true); } @Override public DropSequenceFinalStep dropSequence(Sequence sequence) { - return new DropSequenceImpl(configuration, sequence); + return new DropSequenceImpl(configuration(), sequence); } @Override @@ -1768,7 +1739,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public DropSequenceFinalStep dropSequenceIfExists(Sequence sequence) { - return new DropSequenceImpl(configuration, sequence, true); + return new DropSequenceImpl(configuration(), sequence, true); } @Override @@ -1778,7 +1749,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public TruncateIdentityStep truncate(Table table) { - return new TruncateImpl(configuration, table); + return new TruncateImpl(configuration(), table); } // ------------------------------------------------------------------------- @@ -1787,7 +1758,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public BigInteger lastID() { - switch (configuration.dialect().family()) { + switch (configuration().dialect().family()) { case DERBY: { Field field = field("identity_val_local()", BigInteger.class); return select(field).fetchOne(field); @@ -1831,7 +1802,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { xx [/pro] */ default: - throw new SQLDialectNotSupportedException("identity functionality not supported by " + configuration.dialect()); + throw new SQLDialectNotSupportedException("identity functionality not supported by " + configuration().dialect()); } } @@ -1863,7 +1834,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Record newRecord(Field... fields) { - return Utils.newRecord(false, RecordImpl.class, fields, configuration).operate(null); + return Utils.newRecord(false, RecordImpl.class, fields, configuration()).operate(null); } // [jooq-tools] START [newRecord] @@ -2004,17 +1975,17 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public > R newRecord(UDT type) { - return Utils.newRecord(false, type, configuration).operate(null); + return Utils.newRecord(false, type, configuration()).operate(null); } @Override public R newRecord(Table table) { - return Utils.newRecord(false, table, configuration).operate(null); + return Utils.newRecord(false, table, configuration()).operate(null); } @Override public R newRecord(Table table, final Object source) { - return Utils.newRecord(false, table, configuration) + return Utils.newRecord(false, table, configuration()) .operate(new RecordOperation() { @Override @@ -2027,12 +1998,12 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public Result newResult(Table table) { - return new ResultImpl(configuration, table.fields()); + return new ResultImpl(configuration(), table.fields()); } @Override public Result newResult(Field... fields) { - return new ResultImpl(configuration, fields); + return new ResultImpl(configuration(), fields); } // [jooq-tools] START [newResult] @@ -2180,7 +2151,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { final Configuration previous = Utils.getConfiguration(query); try { - query.attach(configuration); + query.attach(configuration()); return query.fetch(); } finally { @@ -2193,7 +2164,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { final Configuration previous = Utils.getConfiguration(query); try { - query.attach(configuration); + query.attach(configuration()); return query.fetchLazy(); } finally { @@ -2206,7 +2177,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { final Configuration previous = Utils.getConfiguration(query); try { - query.attach(configuration); + query.attach(configuration()); return query.fetchMany(); } finally { @@ -2219,7 +2190,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { final Configuration previous = Utils.getConfiguration(query); try { - query.attach(configuration); + query.attach(configuration()); return query.fetchOne(); } finally { @@ -2232,7 +2203,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { final Configuration previous = Utils.getConfiguration(query); try { - query.attach(configuration); + query.attach(configuration()); return value1(fetchOne(query)); } finally { @@ -2275,7 +2246,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { final Configuration previous = Utils.getConfiguration(query); try { - query.attach(configuration); + query.attach(configuration()); return query.execute(); } finally { @@ -2383,6 +2354,6 @@ public class DefaultDSLContext implements DSLContext, Serializable { @Override public String toString() { - return configuration.toString(); + return configuration().toString(); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java index 472541d2e7..b326c190e8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java @@ -392,7 +392,7 @@ class DefaultRenderContext extends AbstractContext implements Ren return this; } - SQLDialect family = configuration.dialect().family(); + SQLDialect family = family(); // Quoting is needed when explicitly requested... boolean needsQuote = diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultTransactionContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultTransactionContext.java index c9bf6b9349..c691d27a6d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultTransactionContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultTransactionContext.java @@ -41,42 +41,19 @@ package org.jooq.impl; import org.jooq.Configuration; -import org.jooq.SQLDialect; import org.jooq.Transaction; import org.jooq.TransactionContext; -import org.jooq.conf.Settings; /** * @author Lukas Eder */ -class DefaultTransactionContext implements TransactionContext { +class DefaultTransactionContext extends AbstractScope implements TransactionContext { - private final Configuration configuration; Transaction transaction; Exception cause; DefaultTransactionContext(Configuration configuration) { - this.configuration = configuration; - } - - @Override - public final Configuration configuration() { - return configuration; - } - - @Override - public final Settings settings() { - return Utils.settings(configuration()); - } - - @Override - public final SQLDialect dialect() { - return Utils.configuration(configuration()).dialect(); - } - - @Override - public final SQLDialect family() { - return dialect().family(); + super(configuration); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/ExistsOperator.java b/jOOQ/src/main/java/org/jooq/impl/ExistsOperator.java new file mode 100644 index 0000000000..027098ed26 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/ExistsOperator.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ + +package org.jooq.impl; + +/** + * An operator for the {@link ExistsCondition} + * + * @author Lukas Eder + */ +enum ExistsOperator { + + EXISTS("exists"), + NOT_EXISTS("not exists"); + + private final String sql; + + private ExistsOperator(String sql) { + this.sql = sql; + } + + public String toSQL() { + return sql; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java index de55220f24..f7b448aeb2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java @@ -41,6 +41,7 @@ package org.jooq.impl; +import org.jooq.Binding; import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Context; @@ -61,6 +62,7 @@ class ParameterImpl extends AbstractQueryPart implements Parameter { private final String name; private final DataType type; private final Converter converter; + private final Binding binding; private final boolean isDefaulted; @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -77,6 +79,8 @@ class ParameterImpl extends AbstractQueryPart implements Parameter { : type instanceof ConvertedDataType ? ((ConvertedDataType) type).converter() : new IdentityConverter((Class) type.getType()); + + this.binding = new DefaultBinding(this.converter, type.isLob()); } @Override @@ -89,6 +93,11 @@ class ParameterImpl extends AbstractQueryPart implements Parameter { return converter; } + @Override + public final Binding getBinding() { + return binding; + } + @Override public final DataType getDataType() { return type; diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsExistsCondition.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsExistsCondition.java new file mode 100644 index 0000000000..de27fc3259 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsExistsCondition.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ + +package org.jooq.impl; + +import static org.jooq.Clause.CONDITION; +import static org.jooq.Clause.CONDITION_EXISTS; +import static org.jooq.Clause.CONDITION_NOT_EXISTS; +import static org.jooq.impl.ExistsOperator.EXISTS; + +import org.jooq.Clause; +import org.jooq.Context; +import org.jooq.Select; + +/** + * @author Lukas Eder + */ +class SelectQueryAsExistsCondition extends AbstractCondition { + + private static final long serialVersionUID = 5678338161136603292L; + private static final Clause[] CLAUSES_EXISTS = { CONDITION, CONDITION_EXISTS }; + private static final Clause[] CLAUSES_EXISTS_NOT = { CONDITION, CONDITION_NOT_EXISTS }; + + private final Select query; + private final ExistsOperator operator; + + SelectQueryAsExistsCondition(Select query, ExistsOperator operator) { + this.query = query; + this.operator = operator; + } + + @Override + public final void accept(Context ctx) { + + // If this is already a subquery, proceed + if (ctx.subquery()) { + ctx.keyword(operator.toSQL()) + .sql(" (") + .formatIndentStart() + .formatNewLine() + .visit(query) + .formatIndentEnd() + .formatNewLine() + .sql(")"); + } + else { + ctx.keyword(operator.toSQL()) + .sql(" (") + .subquery(true) + .formatIndentStart() + .formatNewLine() + .visit(query) + .formatIndentEnd() + .formatNewLine() + .subquery(false) + .sql(")"); + } + } + + @Override + public final Clause[] clauses(Context ctx) { + return operator == EXISTS ? CLAUSES_EXISTS : CLAUSES_EXISTS_NOT; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsField.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsField.java new file mode 100644 index 0000000000..01b1633dfa --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsField.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ + +package org.jooq.impl; + +import org.jooq.Context; +import org.jooq.DataType; +import org.jooq.Field; +import org.jooq.Select; + +/** + * @author Lukas Eder + */ +class SelectQueryAsField extends AbstractField { + + private static final long serialVersionUID = 3463144434073231750L; + + private final Select query; + + SelectQueryAsField(Select query, DataType type) { + super("select", type); + + this.query = query; + } + + @Override + public final Field as(String alias) { + return new FieldAlias(this, alias); + } + + @Override + public final void accept(Context ctx) { + + // If this is already a subquery, proceed + if (ctx.subquery()) { + ctx.sql("(") + .formatIndentStart() + .formatNewLine() + .visit(query) + .formatIndentEnd() + .formatNewLine() + .sql(")"); + } + else { + ctx.sql("(") + .subquery(true) + .formatIndentStart() + .formatNewLine() + .visit(query) + .formatIndentEnd() + .formatNewLine() + .subquery(false) + .sql(")"); + } + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsSubQueryCondition.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsSubQueryCondition.java new file mode 100644 index 0000000000..d938b68e2f --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsSubQueryCondition.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ + +package org.jooq.impl; + +import static org.jooq.Clause.CONDITION; +import static org.jooq.Clause.CONDITION_COMPARISON; + +import org.jooq.Clause; +import org.jooq.Comparator; +import org.jooq.Context; +import org.jooq.Field; +import org.jooq.Select; + +/** + * @author Lukas Eder + */ +class SelectQueryAsSubQueryCondition extends AbstractCondition { + + private static final long serialVersionUID = -402776705884329740L; + private static final Clause[] CLAUSES = { CONDITION, CONDITION_COMPARISON }; + + private final Select query; + private final Field field; + private final Comparator comparator; + + SelectQueryAsSubQueryCondition(Select query, Field field, Comparator comparator) { + this.query = query; + this.field = field; + this.comparator = comparator; + } + + @Override + public final void accept(Context ctx) { + + // If this is already a subquery, proceed + if (ctx.subquery()) { + ctx.visit(field) + .sql(" ") + .keyword(comparator.toSQL()) + .sql(" (") + .formatIndentStart() + .formatNewLine() + .visit(query) + .formatIndentEnd() + .formatNewLine() + .sql(")"); + } + else { + ctx.visit(field) + .sql(" ") + .keyword(comparator.toSQL()) + .sql(" (") + .subquery(true) + .formatIndentStart() + .formatNewLine() + .visit(query) + .formatIndentEnd() + .formatNewLine() + .subquery(false) + .sql(")"); + } + } + + @Override + public final Clause[] clauses(Context ctx) { + return CLAUSES; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsTable.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsTable.java new file mode 100644 index 0000000000..54b4c13440 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsTable.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ + +package org.jooq.impl; + +import org.jooq.Clause; +import org.jooq.Context; +import org.jooq.Record; +import org.jooq.Select; +import org.jooq.Table; + +/** + * @author Lukas Eder + */ +class SelectQueryAsTable extends AbstractTable { + + private static final long serialVersionUID = 6272398035926615668L; + + private final Select query; + + SelectQueryAsTable(Select query) { + super("select"); + + this.query = query; + } + + final Select query() { + return query; + } + + @Override + public final Table as(String alias) { + return new TableAlias(this, alias, true); + } + + @Override + public final Table as(String alias, String... fieldAliases) { + return new TableAlias(this, alias, fieldAliases, true); + } + + @Override + final Fields fields0() { + return new Fields(query.getSelect()); + } + + @Override + public final Class getRecordType() { + return query.getRecordType(); + } + + @Override + public final void accept(Context ctx) { + + // If this is already a subquery, proceed + if (ctx.subquery()) { + ctx.formatIndentStart() + .formatNewLine() + .visit(query) + .formatIndentEnd() + .formatNewLine(); + } + else { + ctx.subquery(true) + .formatIndentStart() + .formatNewLine() + .visit(query) + .formatIndentEnd() + .formatNewLine() + .subquery(false); + } + } + + @Override + public final Clause[] clauses(Context ctx) { + return null; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index df5cd2c852..99a3c2472f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -1071,7 +1071,7 @@ class SelectQueryImpl extends AbstractResultQuery implement // -------------------------------------------- if (unionOpSize > 0) { unionParenthesis(context, ")"); - + for (int i = 0; i < unionOpSize; i++) { CombineOperator op = unionOp.get(i); diff --git a/jOOQ/src/main/java/org/jooq/impl/UDTRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/UDTRecordImpl.java index b4d054559f..3d837d6047 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UDTRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UDTRecordImpl.java @@ -126,7 +126,9 @@ public class UDTRecordImpl> extends AbstractRecord implem } private final void setValue(Configuration configuration, SQLInput stream, Field field) throws SQLException { - setValue(field, Utils.getFromSQLInput(configuration, stream, field)); + DefaultBindingGetSQLInputContext out = new DefaultBindingGetSQLInputContext(configuration, stream); + field.getBinding().get(out); + setValue(field, out.value()); } @Override @@ -137,7 +139,7 @@ public class UDTRecordImpl> extends AbstractRecord implem } private final void setValue(SQLOutput stream, Field field) throws SQLException { - Utils.writeToSQLOutput(stream, field, getValue(field)); + field.getBinding().set(new DefaultBindingSetSQLOutputContext(localConfiguration(), stream, getValue(field))); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/Utils.java b/jOOQ/src/main/java/org/jooq/impl/Utils.java index f66c3411a5..de22095ec3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Utils.java +++ b/jOOQ/src/main/java/org/jooq/impl/Utils.java @@ -42,10 +42,8 @@ package org.jooq.impl; import static java.lang.Boolean.FALSE; // ... -import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.MARIADB; import static org.jooq.SQLDialect.MYSQL; -import static org.jooq.SQLDialect.POSTGRES; import static org.jooq.conf.BackslashEscaping.DEFAULT; import static org.jooq.conf.BackslashEscaping.ON; import static org.jooq.conf.ParamType.INLINED; @@ -61,7 +59,6 @@ import static org.jooq.impl.DSL.getDataType; import static org.jooq.impl.DSL.nullSafe; import static org.jooq.impl.DSL.val; import static org.jooq.impl.DefaultExecuteContext.localConnection; -import static org.jooq.impl.DefaultExecuteContext.localTargetConnection; import static org.jooq.impl.DropStatementType.INDEX; import static org.jooq.impl.DropStatementType.SEQUENCE; import static org.jooq.impl.DropStatementType.TABLE; @@ -70,34 +67,17 @@ import static org.jooq.impl.Identifiers.QUOTES; import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER; import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER_ESCAPED; import static org.jooq.impl.Identifiers.QUOTE_START_DELIMITER; -import static org.jooq.tools.jdbc.JDBCUtils.safeFree; -import static org.jooq.tools.jdbc.JDBCUtils.wasNull; import static org.jooq.tools.reflect.Reflect.accessible; -import static org.jooq.tools.reflect.Reflect.on; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.Array; -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; import java.sql.Connection; -import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.SQLInput; -import java.sql.SQLOutput; import java.sql.Statement; -import java.sql.Time; -import java.sql.Timestamp; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; @@ -105,7 +85,6 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -121,17 +100,14 @@ import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Context; -import org.jooq.Converter; import org.jooq.Cursor; import org.jooq.DSLContext; import org.jooq.DataType; -import org.jooq.EnumType; import org.jooq.ExecuteContext; import org.jooq.ExecuteListener; import org.jooq.Field; import org.jooq.Name; import org.jooq.Param; -import org.jooq.Parameter; import org.jooq.QueryPart; import org.jooq.Record; import org.jooq.RecordType; @@ -149,19 +125,10 @@ import org.jooq.conf.Settings; import org.jooq.exception.DataAccessException; import org.jooq.exception.InvalidResultException; import org.jooq.impl.Utils.Cache.CachedOperation; -import org.jooq.tools.Convert; import org.jooq.tools.JooqLogger; import org.jooq.tools.StringUtils; import org.jooq.tools.jdbc.JDBCUtils; import org.jooq.tools.reflect.Reflect; -import org.jooq.types.DayToSecond; -import org.jooq.types.UByte; -import org.jooq.types.UInteger; -import org.jooq.types.ULong; -import org.jooq.types.UNumber; -import org.jooq.types.UShort; -import org.jooq.types.YearToMonth; -import org.jooq.util.postgres.PostgresUtils; /** * General internal jOOQ utilities @@ -2310,1031 +2277,6 @@ final class Utils { listener.warning(ctx); } - @SuppressWarnings("unchecked") - static final U getFromSQLInput(Configuration configuration, SQLInput stream, Field field) throws SQLException { - DataType dataType = field.getDataType(); - - // [#650] [#2155] [#3108] Use the Field's Converter before actually binding any value - Converter converter = (Converter) field.getConverter(); - Class type = converter.fromType(); - - T result = null; - - if (type == Blob.class) { - result = (T) stream.readBlob(); - } - else if (type == Boolean.class) { - result = (T) wasNull(stream, Boolean.valueOf(stream.readBoolean())); - } - else if (type == BigInteger.class) { - BigDecimal d = stream.readBigDecimal(); - result = (T) (d == null ? null : d.toBigInteger()); - } - else if (type == BigDecimal.class) { - result = (T) stream.readBigDecimal(); - } - else if (type == Byte.class) { - result = (T) wasNull(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(); - result = (T) (blob == null ? null : blob.getBytes(1, (int) blob.length())); - } - finally { - safeFree(blob); - } - } - else { - result = (T) stream.readBytes(); - } - } - else if (type == Clob.class) { - result = (T) stream.readClob(); - } - else if (type == Date.class) { - result = (T) stream.readDate(); - } - else if (type == Double.class) { - result = (T) wasNull(stream, Double.valueOf(stream.readDouble())); - } - else if (type == Float.class) { - result = (T) wasNull(stream, Float.valueOf(stream.readFloat())); - } - else if (type == Integer.class) { - result = (T) wasNull(stream, Integer.valueOf(stream.readInt())); - } - else if (type == Long.class) { - result = (T) wasNull(stream, Long.valueOf(stream.readLong())); - } - else if (type == Short.class) { - result = (T) wasNull(stream, Short.valueOf(stream.readShort())); - } - else if (type == String.class) { - result = (T) stream.readString(); - } - else if (type == Time.class) { - result = (T) stream.readTime(); - } - else if (type == Timestamp.class) { - result = (T) stream.readTimestamp(); - } - else if (type == YearToMonth.class) { - String string = stream.readString(); - result = (T) (string == null ? null : YearToMonth.valueOf(string)); - } - else if (type == DayToSecond.class) { - String string = stream.readString(); - result = (T) (string == null ? null : DayToSecond.valueOf(string)); - } - else if (type == UByte.class) { - String string = stream.readString(); - result = (T) (string == null ? null : UByte.valueOf(string)); - } - else if (type == UShort.class) { - String string = stream.readString(); - result = (T) (string == null ? null : UShort.valueOf(string)); - } - else if (type == UInteger.class) { - String string = stream.readString(); - result = (T) (string == null ? null : UInteger.valueOf(string)); - } - else if (type == ULong.class) { - String string = stream.readString(); - result = (T) (string == null ? null : ULong.valueOf(string)); - } - else if (type == UUID.class) { - result = (T) Convert.convert(stream.readString(), UUID.class); - } - - // The type byte[] is handled earlier. byte[][] can be handled here - else if (type.isArray()) { - Array array = stream.readArray(); - result = (T) (array == null ? null : array.getArray()); - } - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxx x xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx - x - xx [/pro] */ - else if (EnumType.class.isAssignableFrom(type)) { - result = getEnumType(type, stream.readString()); - } - else if (UDTRecord.class.isAssignableFrom(type)) { - result = (T) stream.readObject(); - } - else { - result = (T) unlob(stream.readObject()); - } - - return converter.from(result); - } - - @SuppressWarnings("unchecked") - static final void writeToSQLOutput(SQLOutput stream, Field field, U v) throws SQLException { - DataType dataType = field.getDataType(); - - // [#650] [#2155] [#3108] Use the Field's Converter before actually binding any value - Converter converter = (Converter) field.getConverter(); - Class type = converter.fromType(); - T value = converter.to(v); - - if (value == null) { - stream.writeObject(null); - } - else if (type == Blob.class) { - stream.writeBlob((Blob) value); - } - else if (type == Boolean.class) { - stream.writeBoolean((Boolean) value); - } - else if (type == BigInteger.class) { - stream.writeBigDecimal(new BigDecimal((BigInteger) value)); - } - else if (type == BigDecimal.class) { - stream.writeBigDecimal((BigDecimal) value); - } - else if (type == Byte.class) { - stream.writeByte((Byte) value); - } - else if (type == byte[].class) { - - // [#1327] Oracle cannot serialise BLOBs as byte[] to SQLOutput - // Use reflection to avoid dependency on OJDBC - if (dataType.isLob()) { - Blob blob = null; - - try { - blob = on("oracle.sql.BLOB").call("createTemporary", - on(stream).call("getSTRUCT") - .call("getJavaSqlConnection").get(), - false, - on("oracle.sql.BLOB").get("DURATION_SESSION")).get(); - - blob.setBytes(1, (byte[]) value); - stream.writeBlob(blob); - } - finally { - DefaultExecuteContext.register(blob); - } - } - else { - stream.writeBytes((byte[]) value); - } - } - else if (type == Clob.class) { - stream.writeClob((Clob) value); - } - else if (type == Date.class) { - stream.writeDate((Date) value); - } - else if (type == Double.class) { - stream.writeDouble((Double) value); - } - else if (type == Float.class) { - stream.writeFloat((Float) value); - } - else if (type == Integer.class) { - stream.writeInt((Integer) value); - } - else if (type == Long.class) { - stream.writeLong((Long) value); - } - else if (type == Short.class) { - stream.writeShort((Short) value); - } - else if (type == String.class) { - - // [#1327] Oracle cannot serialise CLOBs as String to SQLOutput - // Use reflection to avoid dependency on OJDBC - if (dataType.isLob()) { - Clob clob = null; - - try { - clob = on("oracle.sql.CLOB").call("createTemporary", - on(stream).call("getSTRUCT") - .call("getJavaSqlConnection").get(), - false, - on("oracle.sql.CLOB").get("DURATION_SESSION")).get(); - - clob.setString(1, (String) value); - stream.writeClob(clob); - } - finally { - DefaultExecuteContext.register(clob); - } - } - else { - stream.writeString((String) value); - } - } - else if (type == Time.class) { - stream.writeTime((Time) value); - } - else if (type == Timestamp.class) { - stream.writeTimestamp((Timestamp) value); - } - else if (type == YearToMonth.class) { - stream.writeString(value.toString()); - } - else if (type == DayToSecond.class) { - stream.writeString(value.toString()); - } -// else if (type.isArray()) { -// stream.writeArray(value); -// } - else if (UNumber.class.isAssignableFrom(type)) { - stream.writeString(value.toString()); - } - else if (type == UUID.class) { - stream.writeString(value.toString()); - } - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - - xx xxxxxxx xx xxx xxxxxx xxxxxx xxxx xxxxxxxxxxxxxxxxxx xxx xxxx - xx xxx xx xxxxxxxxxxxxxxxxxxx xxxxx xx xxxxxxxxxxx xxxxxx xx xxxxxx - xxxxxxxxxxxxxx xxxxxxxxxxx x xxxxxxxxxxxxxxxx xxxxxx - xxxxxxxx xxxxx x xxxxxxxxxxxxxxxxxx - - xx xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxxxxxx x - xxxxxxxx xxxxxxxxx x xxx xxxxxxxxxxxxxxxxxxxxx - - xxx xxxx x x xx x x xxxxxxxxxxxxxxxxx xxxx - xxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - - xxxxx x xxxxxxxxxx - x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx - x - xx [/pro] */ - else if (EnumType.class.isAssignableFrom(type)) { - stream.writeString(((EnumType) value).getLiteral()); - } - else if (UDTRecord.class.isAssignableFrom(type)) { - stream.writeObject((UDTRecord) value); - } - else { - throw new UnsupportedOperationException("Type " + type + " is not supported"); - } - } - - static final U getFromResultSet(ExecuteContext ctx, Field field, int index) throws SQLException { - - @SuppressWarnings("unchecked") - Converter converter = (Converter) field.getConverter(); - return converter.from(getFromResultSet(ctx, converter.fromType(), index)); - } - - @SuppressWarnings("unchecked") - private static final T getFromResultSet(ExecuteContext ctx, Class type, int index) throws SQLException { - - ResultSet rs = ctx.resultSet(); - - if (type == Blob.class) { - return (T) rs.getBlob(index); - } - else if (type == Boolean.class) { - return (T) wasNull(rs, Boolean.valueOf(rs.getBoolean(index))); - } - else if (type == BigInteger.class) { - // The SQLite JDBC driver doesn't support BigDecimals - if (ctx.configuration().dialect() == SQLDialect.SQLITE) { - return Convert.convert(rs.getString(index), (Class) BigInteger.class); - } - else { - BigDecimal result = rs.getBigDecimal(index); - return (T) (result == null ? null : result.toBigInteger()); - } - } - else if (type == BigDecimal.class) { - // The SQLite JDBC driver doesn't support BigDecimals - if (ctx.configuration().dialect() == SQLDialect.SQLITE) { - return Convert.convert(rs.getString(index), (Class) BigDecimal.class); - } - else { - return (T) rs.getBigDecimal(index); - } - } - else if (type == Byte.class) { - return (T) wasNull(rs, Byte.valueOf(rs.getByte(index))); - } - else if (type == byte[].class) { - return (T) rs.getBytes(index); - } - else if (type == Clob.class) { - return (T) rs.getClob(index); - } - else if (type == Date.class) { - return (T) getDate(ctx.configuration().dialect(), rs, index); - } - else if (type == Double.class) { - return (T) wasNull(rs, Double.valueOf(rs.getDouble(index))); - } - else if (type == Float.class) { - return (T) wasNull(rs, Float.valueOf(rs.getFloat(index))); - } - else if (type == Integer.class) { - return (T) wasNull(rs, Integer.valueOf(rs.getInt(index))); - } - else if (type == Long.class) { - return (T) wasNull(rs, Long.valueOf(rs.getLong(index))); - } - else if (type == Short.class) { - return (T) wasNull(rs, Short.valueOf(rs.getShort(index))); - } - else if (type == String.class) { - return (T) rs.getString(index); - } - else if (type == Time.class) { - return (T) getTime(ctx.configuration().dialect(), rs, index); - } - else if (type == Timestamp.class) { - return (T) getTimestamp(ctx.configuration().dialect(), rs, index); - } - else if (type == YearToMonth.class) { - if (ctx.configuration().dialect() == POSTGRES) { - Object object = rs.getObject(index); - return (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); - } - else { - String string = rs.getString(index); - return (T) (string == null ? null : YearToMonth.valueOf(string)); - } - } - else if (type == DayToSecond.class) { - if (ctx.configuration().dialect() == POSTGRES) { - Object object = rs.getObject(index); - return (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); - } - else { - String string = rs.getString(index); - return (T) (string == null ? null : DayToSecond.valueOf(string)); - } - } - else if (type == UByte.class) { - return (T) Convert.convert(rs.getString(index), UByte.class); - } - else if (type == UShort.class) { - return (T) Convert.convert(rs.getString(index), UShort.class); - } - else if (type == UInteger.class) { - return (T) Convert.convert(rs.getString(index), UInteger.class); - } - else if (type == ULong.class) { - return (T) Convert.convert(rs.getString(index), ULong.class); - } - else if (type == UUID.class) { - switch (ctx.configuration().dialect().family()) { - - // [#1624] Some JDBC drivers natively support the - // java.util.UUID data type - case H2: - case POSTGRES: { - return (T) rs.getObject(index); - } - - /* [pro] xx - xx xxxxx xxx xxxxxxxx xxxx xxxx xxxxx xx xx xxxx xxxx xxxxxxxx - xx xxxx xx xxxx xxxxxxxxxx xxxxxxx xxxx xxxxxxxxxxxxxxxxxx - xxxx xxxxxxxxxx - xxxx xxxxxxx - - xx [/pro] */ - // Most databases don't have such a type. In this case, jOOQ - // simulates the type - default: { - return (T) Convert.convert(rs.getString(index), UUID.class); - } - } - } - - // The type byte[] is handled earlier. byte[][] can be handled here - else if (type.isArray()) { - switch (ctx.configuration().dialect()) { - case POSTGRES: { - return pgGetArray(ctx, type, index); - } - - default: - // Note: due to a HSQLDB bug, it is not recommended to call rs.getObject() here: - // See https://sourceforge.net/tracker/?func=detail&aid=3181365&group_id=23316&atid=378131 - return (T) convertArray(rs.getArray(index), (Class) type); - } - } - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxx xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx - x - xx [/pro] */ - else if (EnumType.class.isAssignableFrom(type)) { - return getEnumType(type, rs.getString(index)); - } - else if (UDTRecord.class.isAssignableFrom(type)) { - switch (ctx.configuration().dialect()) { - case POSTGRES: - return (T) pgNewUDTRecord(type, rs.getObject(index)); - } - - return (T) rs.getObject(index, DataTypes.udtRecords()); - } - else if (Result.class.isAssignableFrom(type)) { - ResultSet nested = (ResultSet) rs.getObject(index); - return (T) DSL.using(ctx.configuration()).fetch(nested); - } - else { - return (T) unlob(rs.getObject(index)); - } - } - - /** - * [#2534] Extract byte[] or String data from a - * LOB, if the argument is a lob. - */ - private static Object unlob(Object object) throws SQLException { - if (object instanceof Blob) { - Blob blob = (Blob) object; - - try { - return blob.getBytes(1, (int) blob.length()); - } - finally { - JDBCUtils.safeFree(blob); - } - } - else if (object instanceof Clob) { - Clob clob = (Clob) object; - - try { - return clob.getSubString(1, (int) clob.length()); - } - finally { - JDBCUtils.safeFree(clob); - } - } - - return object; - } - - /* [pro] xx - xxxxxxx xxxxxx xxxxx xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxx xxxxx xxxxxx xxxxxxx xxxxxxx xxxxxxxxxxxxxxx xxxxx xxxxxx xxxxxxxxxxxx x - xx xxxxxx xx xxxxx x - xxxxxx xxxxx - x - xxxx x - xx xxxxx xxxxxx xxx xxxxx xxxxxx xxxx xxxx xxxxxxx - xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxx - x - x - - xxxxxx xxxxx xxx xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxx xxxxxxx xxxxxx xxxxxxxxxxxx x - xx xxxxxxx xx xxxxx x - xxxxxxxxxxxxxxxx xxxxxx - x - xxxx x - xx xxxxxx xxxxx xxxxxx xxxx xx xxxxxx xx xxxx xxxxx xx xxxxxx - xx xxxxxx xxxxx xxxx xxxx xx xxxx xx xxxxxxx xxxx xx xxxxxx xx - xx xxxxxxxxx xxxxxxx xxx xxxxxx xxxxxxx - xxxxxxxx xxxxx x xxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xxx xxxxxxxxx x xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxx - - xxx xxxx x x xx x x xxxxxxxxxxxxx xxxx - xxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - - xxxxxxxxxxxxxxxxxxxxxx - xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - x - - xxxxxx xxxxxxx - x - - xx [/pro] */ - private static final Object[] convertArray(Object array, Class type) throws SQLException { - if (array instanceof Object[]) { - return Convert.convert(array, type); - } - else if (array instanceof Array) { - return convertArray((Array) array, type); - } - - return null; - } - - private static final Object[] convertArray(Array array, Class type) throws SQLException { - if (array != null) { - return Convert.convert(array.getArray(), type); - } - - return null; - } - - private static final Date getDate(SQLDialect dialect, ResultSet rs, int index) throws SQLException { - - // SQLite's type affinity needs special care... - if (dialect == SQLDialect.SQLITE) { - String date = rs.getString(index); - - if (date != null) { - return new Date(parse("yyyy-MM-dd", date)); - } - - return null; - } - - // Cubrid SQL dates are incorrectly fetched. Reset milliseconds... - // See http://jira.cubrid.org/browse/APIS-159 - // See https://sourceforge.net/apps/trac/cubridinterface/ticket/140 - else if (dialect == CUBRID) { - Date date = rs.getDate(index); - - if (date != null) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(date.getTime()); - cal.set(Calendar.MILLISECOND, 0); - date = new Date(cal.getTimeInMillis()); - } - - return date; - } - - else { - return rs.getDate(index); - } - } - - private static final Time getTime(SQLDialect dialect, ResultSet rs, int index) throws SQLException { - - // SQLite's type affinity needs special care... - if (dialect == SQLDialect.SQLITE) { - String time = rs.getString(index); - - if (time != null) { - return new Time(parse("HH:mm:ss", time)); - } - - return null; - } - - // Cubrid SQL dates are incorrectly fetched. Reset milliseconds... - // See http://jira.cubrid.org/browse/APIS-159 - // See https://sourceforge.net/apps/trac/cubridinterface/ticket/140 - else if (dialect == CUBRID) { - Time time = rs.getTime(index); - - if (time != null) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(time.getTime()); - cal.set(Calendar.MILLISECOND, 0); - time = new Time(cal.getTimeInMillis()); - } - - return time; - } - - else { - return rs.getTime(index); - } - } - - private static final Timestamp getTimestamp(SQLDialect dialect, ResultSet rs, int index) throws SQLException { - - // SQLite's type affinity needs special care... - if (dialect == SQLDialect.SQLITE) { - String timestamp = rs.getString(index); - - if (timestamp != null) { - return new Timestamp(parse("yyyy-MM-dd HH:mm:ss", timestamp)); - } - - return null; - } else { - return rs.getTimestamp(index); - } - } - - private static final long parse(String pattern, String date) throws SQLException { - try { - - // Try reading a plain number first - try { - return Long.valueOf(date); - } - - // If that fails, try reading a formatted date - catch (NumberFormatException e) { - return new SimpleDateFormat(pattern).parse(date).getTime(); - } - } - catch (ParseException e) { - throw new SQLException("Could not parse date " + date, e); - } - } - - @SuppressWarnings("unchecked") - private static final T getEnumType(Class type, String literal) throws SQLException { - try { - Object[] list = (Object[]) type.getMethod("values").invoke(type); - - for (Object e : list) { - String l = ((EnumType) e).getLiteral(); - - if (l.equals(literal)) { - return (T) e; - } - } - } - catch (Exception e) { - throw new SQLException("Unknown enum literal found : " + literal); - } - - return null; - } - - @SuppressWarnings("unchecked") - static final U getFromStatement(ExecuteContext ctx, Parameter parameter, int index) throws SQLException { - CallableStatement stmt = (CallableStatement) ctx.statement(); - - // [#650] [#2155] [#3108] Use the Field's Converter before actually binding any value - Converter converter = (Converter) parameter.getConverter(); - Class type = converter.fromType(); - - T result = null; - - if (type == Blob.class) { - result = (T) stmt.getBlob(index); - } - else if (type == Boolean.class) { - result = (T) wasNull(stmt, Boolean.valueOf(stmt.getBoolean(index))); - } - else if (type == BigInteger.class) { - BigDecimal d = stmt.getBigDecimal(index); - result = (T) (d == null ? null : d.toBigInteger()); - } - else if (type == BigDecimal.class) { - result = (T) stmt.getBigDecimal(index); - } - else if (type == Byte.class) { - result = (T) wasNull(stmt, Byte.valueOf(stmt.getByte(index))); - } - else if (type == byte[].class) { - result = (T) stmt.getBytes(index); - } - else if (type == Clob.class) { - result = (T) stmt.getClob(index); - } - else if (type == Date.class) { - result = (T) stmt.getDate(index); - } - else if (type == Double.class) { - result = (T) wasNull(stmt, Double.valueOf(stmt.getDouble(index))); - } - else if (type == Float.class) { - result = (T) wasNull(stmt, Float.valueOf(stmt.getFloat(index))); - } - else if (type == Integer.class) { - result = (T) wasNull(stmt, Integer.valueOf(stmt.getInt(index))); - } - else if (type == Long.class) { - result = (T) wasNull(stmt, Long.valueOf(stmt.getLong(index))); - } - else if (type == Short.class) { - result = (T) wasNull(stmt, Short.valueOf(stmt.getShort(index))); - } - else if (type == String.class) { - result = (T) stmt.getString(index); - } - else if (type == Time.class) { - result = (T) stmt.getTime(index); - } - else if (type == Timestamp.class) { - result = (T) stmt.getTimestamp(index); - } - else if (type == YearToMonth.class) { - if (ctx.configuration().dialect() == POSTGRES) { - Object object = stmt.getObject(index); - result = (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); - } - else { - String string = stmt.getString(index); - result = (T) (string == null ? null : YearToMonth.valueOf(string)); - } - } - else if (type == DayToSecond.class) { - if (ctx.configuration().dialect() == POSTGRES) { - Object object = stmt.getObject(index); - result = (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); - } - else { - String string = stmt.getString(index); - result = (T) (string == null ? null : DayToSecond.valueOf(string)); - } - } - else if (type == UByte.class) { - String string = stmt.getString(index); - result = (T) (string == null ? null : UByte.valueOf(string)); - } - else if (type == UShort.class) { - String string = stmt.getString(index); - result = (T) (string == null ? null : UShort.valueOf(string)); - } - else if (type == UInteger.class) { - String string = stmt.getString(index); - result = (T) (string == null ? null : UInteger.valueOf(string)); - } - else if (type == ULong.class) { - String string = stmt.getString(index); - result = (T) (string == null ? null : ULong.valueOf(string)); - } - else if (type == UUID.class) { - switch (ctx.configuration().dialect().family()) { - - // [#1624] Some JDBC drivers natively support the - // java.util.UUID data type - case H2: - case POSTGRES: { - result = (T) stmt.getObject(index); - break; - } - - /* [pro] xx - xx xxxxx xxx xxxxxxxx xxxx xxxx xxxxx xx xx xxxx xxxx xxxxxxxx - xx xxxx xx xxxx xxxxxxxxxx xxxxxxx xxxx xxxxxxxxxxxxxxxxxx - xxxx xxxxxxxxxx - xxxx xxxxxxx - - xx [/pro] */ - // Most databases don't have such a type. In this case, jOOQ - // simulates the type - default: { - result = (T) Convert.convert(stmt.getString(index), UUID.class); - break; - } - } - } - - // The type byte[] is handled earlier. byte[][] can be handled here - else if (type.isArray()) { - result = (T) convertArray(stmt.getObject(index), (Class)type); - } - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxx x xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx - x - xx [/pro] */ - else if (EnumType.class.isAssignableFrom(type)) { - result = getEnumType(type, stmt.getString(index)); - } - else if (UDTRecord.class.isAssignableFrom(type)) { - switch (ctx.configuration().dialect()) { - case POSTGRES: - result = (T) pgNewUDTRecord(type, stmt.getObject(index)); - break; - - default: - result = (T) stmt.getObject(index, DataTypes.udtRecords()); - break; - } - } - else if (Result.class.isAssignableFrom(type)) { - ResultSet nested = (ResultSet) stmt.getObject(index); - result = (T) DSL.using(ctx.configuration()).fetch(nested); - } - else { - result = (T) stmt.getObject(index); - } - - return converter.from(result); - } - - // ------------------------------------------------------------------------- - // XXX: The following section has been added for Postgres UDT support. The - // official Postgres JDBC driver does not implement SQLData and similar - // interfaces. Instead, a string representation of a UDT has to be parsed - // ------------------------------------------------------------------------- - - @SuppressWarnings("unchecked") - private static final T pgFromString(Class type, String string) throws SQLException { - if (string == null) { - return null; - } - else if (type == Blob.class) { - // Not supported - } - else if (type == Boolean.class) { - return (T) Boolean.valueOf(string); - } - else if (type == BigInteger.class) { - return (T) new BigInteger(string); - } - else if (type == BigDecimal.class) { - return (T) new BigDecimal(string); - } - else if (type == Byte.class) { - return (T) Byte.valueOf(string); - } - else if (type == byte[].class) { - return (T) PostgresUtils.toBytes(string); - } - else if (type == Clob.class) { - // Not supported - } - else if (type == Date.class) { - SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd"); - return (T) new Date(pgParseDate(string, f).getTime()); - } - else if (type == Double.class) { - return (T) Double.valueOf(string); - } - else if (type == Float.class) { - return (T) Float.valueOf(string); - } - else if (type == Integer.class) { - return (T) Integer.valueOf(string); - } - else if (type == Long.class) { - return (T) Long.valueOf(string); - } - else if (type == Short.class) { - return (T) Short.valueOf(string); - } - else if (type == String.class) { - return (T) string; - } - else if (type == Time.class) { - SimpleDateFormat f = new SimpleDateFormat("HH:mm:ss"); - return (T) new Time(pgParseDate(string, f).getTime()); - } - else if (type == Timestamp.class) { - SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - return (T) new Timestamp(pgParseDate(string, f).getTime()); - } - else if (type == UByte.class) { - return (T) UByte.valueOf(string); - } - else if (type == UShort.class) { - return (T) UShort.valueOf(string); - } - else if (type == UInteger.class) { - return (T) UInteger.valueOf(string); - } - else if (type == ULong.class) { - return (T) ULong.valueOf(string); - } - else if (type == UUID.class) { - return (T) UUID.fromString(string); - } - else if (type.isArray()) { - return (T) pgNewArray(type, string); - } - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xx xxx xxxxxxxxx - x - xx [/pro] */ - else if (EnumType.class.isAssignableFrom(type)) { - return getEnumType(type, string); - } - else if (UDTRecord.class.isAssignableFrom(type)) { - return (T) pgNewUDTRecord(type, string); - } - - throw new UnsupportedOperationException("Class " + type + " is not supported"); - } - - private static final java.util.Date pgParseDate(String string, SimpleDateFormat f) throws SQLException { - try { - return f.parse(string); - } - catch (ParseException e) { - throw new SQLException(e); - } - } - - /** - * Create a UDT record from a PGobject - *

- * Unfortunately, this feature is very poorly documented and true UDT - * support by the PostGreSQL JDBC driver has been postponed for a long time. - * - * @param object An object of type PGobject. The actual argument type cannot - * be expressed in the method signature, as no explicit - * dependency to postgres logic is desired - * @return The converted {@link UDTRecord} - */ - @SuppressWarnings("unchecked") - private static final UDTRecord pgNewUDTRecord(Class type, final Object object) throws SQLException { - if (object == null) { - return null; - } - - return Utils.newRecord(true, (Class>) type) - .operate(new RecordOperation, SQLException>() { - - @Override - public UDTRecord operate(UDTRecord record) throws SQLException { - List values = PostgresUtils.toPGObject(object.toString()); - - Row row = record.fieldsRow(); - for (int i = 0; i < row.size(); i++) { - pgSetValue(record, row.field(i), values.get(i)); - } - - return record; - } - }); - } - - /** - * Workarounds for the unimplemented Postgres JDBC driver features - */ - @SuppressWarnings("unchecked") - private static final T pgGetArray(ExecuteContext ctx, Class type, int index) throws SQLException { - - ResultSet rs = ctx.resultSet(); - - // Get the JDBC Array and check for null. If null, that's OK - Array array = rs.getArray(index); - if (array == null) { - return null; - } - - // Try fetching a Java Object[]. That's gonna work for non-UDT types - try { - return (T) convertArray(rs.getArray(index), (Class) type); - } - - // This might be a UDT (not implemented exception...) - catch (Exception e) { - List result = new ArrayList(); - - // Try fetching the array as a JDBC ResultSet - try { - ctx.resultSet(array.getResultSet()); - while (ctx.resultSet().next()) { - result.add(getFromResultSet(ctx, type.getComponentType(), 2)); - } - } - - // That might fail too, then we don't know any further... - catch (Exception fatal) { - log.error("Cannot parse Postgres array: " + rs.getString(index)); - log.error(fatal); - return null; - } - - finally { - ctx.resultSet(rs); - } - - return (T) convertArray(result.toArray(), (Class) type); - } - } - - /** - * Create an array from a String - *

- * Unfortunately, this feature is very poorly documented and true UDT - * support by the PostGreSQL JDBC driver has been postponed for a long time. - * - * @param string A String representation of an array - * @return The converted array - */ - private static final Object[] pgNewArray(Class type, String string) throws SQLException { - if (string == null) { - return null; - } - - try { - Class component = type.getComponentType(); - String values = string.replaceAll("^\\{(.*)\\}$", "$1"); - - if ("".equals(values)) { - return (Object[]) java.lang.reflect.Array.newInstance(component, 0); - } - else { - String[] split = values.split(","); - Object[] result = (Object[]) java.lang.reflect.Array.newInstance(component, split.length); - - for (int i = 0; i < split.length; i++) { - result[i] = pgFromString(type.getComponentType(), split[i]); - } - - return result; - } - } - catch (Exception e) { - throw new SQLException(e); - } - } - - private static final void pgSetValue(UDTRecord record, Field field, String value) throws SQLException { - record.setValue(field, pgFromString(field.getType(), value)); - } - static List parseTXT(String string, String nullLiteral) { String[] strings = string.split("[\\r\\n]+"); diff --git a/jOOQ/src/main/java/org/jooq/impl/Val.java b/jOOQ/src/main/java/org/jooq/impl/Val.java index 31a4c371c1..e343e4eed4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Val.java +++ b/jOOQ/src/main/java/org/jooq/impl/Val.java @@ -40,51 +40,10 @@ */ package org.jooq.impl; -import static java.lang.Boolean.TRUE; -import static java.lang.Integer.toOctalString; -import static java.util.Arrays.asList; -// ... -// ... -import static org.jooq.SQLDialect.CUBRID; -// ... -import static org.jooq.SQLDialect.DERBY; -import static org.jooq.SQLDialect.FIREBIRD; -import static org.jooq.SQLDialect.H2; -import static org.jooq.SQLDialect.HSQLDB; -// ... -// ... -import static org.jooq.SQLDialect.MARIADB; -import static org.jooq.SQLDialect.MYSQL; -// ... -import static org.jooq.SQLDialect.POSTGRES; -import static org.jooq.SQLDialect.SQLITE; -// ... -// ... -import static org.jooq.conf.ParamType.NAMED; -import static org.jooq.conf.ParamType.NAMED_OR_INLINED; -import static org.jooq.impl.DSL.name; -import static org.jooq.impl.DSL.using; -import static org.jooq.impl.Utils.needsBackslashEscaping; -import static org.jooq.tools.StringUtils.leftPad; - -import java.math.BigDecimal; -import java.sql.Date; -import java.sql.Time; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; - -// ... -import org.jooq.BindContext; import org.jooq.Context; -import org.jooq.Converter; import org.jooq.DataType; -import org.jooq.EnumType; import org.jooq.RenderContext; -import org.jooq.SQLDialect; -import org.jooq.Schema; -import org.jooq.UDTRecord; -import org.jooq.tools.StringUtils; -import org.jooq.types.Interval; +import org.jooq.conf.ParamType; /** * @author Lukas Eder @@ -92,7 +51,6 @@ import org.jooq.types.Interval; class Val extends AbstractParam { private static final long serialVersionUID = 6807729087019209084L; - private static final char[] HEX = "0123456789abcdef".toCharArray(); Val(T value, DataType type) { super(value, type); @@ -108,580 +66,22 @@ class Val extends AbstractParam { @Override public void accept(Context ctx) { - if (ctx instanceof RenderContext) - toSQL0((RenderContext) ctx); - else - bind0((BindContext) ctx); - } + if (ctx instanceof RenderContext) { + ParamType paramType = ctx.paramType(); - final void toSQL0(RenderContext context) { + if (isInline(ctx)) + ctx.paramType(ParamType.INLINED); - // Casting can be enforced or prevented - switch (context.castMode()) { - case NEVER: - toSQL(context, value, getConverter()); - return; - - case ALWAYS: - toSQLCast(context); - return; - } - - // See if we "should" cast, to stay on the safe side - if (shouldCast(context)) { - toSQLCast(context); - } - - // Most RDBMS can infer types for bind values - else { - toSQL(context, value, getConverter()); - } - } - - private final boolean shouldCast(RenderContext context) { - - // In default mode, casting is only done when parameters are NOT inlined - if (!isInline(context)) { - - // Generated enums should not be cast... - if (!(value instanceof EnumType)) { - switch (context.configuration().dialect().family()) { - - // These dialects can hardly detect the type of a bound constant. - /* [pro] xx - xxxx xxxx - xxxx xxxxxxxxx - xx [/pro] */ - case DERBY: - case FIREBIRD: - - // These dialects have some trouble, when they mostly get it right. - case H2: - case HSQLDB: - - // [#1261] There are only a few corner-cases, where this is - // really needed. Check back on related CUBRID bugs - case CUBRID: - - // [#1029] Postgres and [#632] Sybase need explicit casting - // in very rare cases. - /* [pro] xx - xxxx xxxxxxx - xx [/pro] */ - case POSTGRES: { - return true; - } - } - } - } - - // [#566] JDBC doesn't explicitly support interval data types. To be on - // the safe side, always cast these types in those dialects that support - // them - if (getDataType().isInterval()) { - switch (context.configuration().dialect().family()) { - /* [pro] xx - xxxx xxxxxxx - xx [/pro] */ - case POSTGRES: - return true; - } - } - - return false; - } - - /** - * Render the bind variable including a cast, if necessary - */ - private final void toSQLCast(RenderContext context) { - DataType dataType = getDataType(context.configuration()); - DataType type = dataType.getSQLDataType(); - SQLDialect family = context.configuration().dialect().family(); - - // [#822] Some RDBMS need precision / scale information on BigDecimals - if (value != null && getType() == BigDecimal.class && asList(CUBRID, DERBY, FIREBIRD, HSQLDB).contains(family)) { - - // Add precision / scale on BigDecimals - int scale = ((BigDecimal) value).scale(); - int precision = scale + ((BigDecimal) value).precision(); - - // Firebird's max precision is 18 - if (family == FIREBIRD) { - precision = Math.min(precision, 18); - } - - toSQLCast(context, dataType, 0, precision, scale); - } - - // [#1028] Most databases don't know an OTHER type (except H2, HSQLDB). - else if (SQLDataType.OTHER == type) { - - // If the bind value is set, it can be used to derive the cast type - if (value != null) { - toSQLCast(context, DefaultDataType.getDataType(family, value.getClass()), 0, 0, 0); - } - - // [#632] [#722] Current integration tests show that Ingres and - // Sybase can do without casting in most cases. - else if (asList().contains(family)) { - context.sql(getBindVariable(context)); - } - - // Derby and DB2 must have a type associated with NULL. Use VARCHAR - // as a workaround. That's probably not correct in all cases, though - else { - toSQLCast(context, DefaultDataType.getDataType(family, String.class), 0, 0, 0); - } - } - - // [#1029] Postgres generally doesn't need the casting. Only in the - // above case where the type is OTHER - // [#1125] Also with temporal data types, casting is needed some times - // [#1130] TODO type can be null for ARRAY types, etc. - else if (family == POSTGRES && (type == null || !type.isTemporal())) { - toSQL(context, value, getConverter()); - } - - // [#1727] VARCHAR types should be cast to their actual lengths in some - // dialects - else if ((type == SQLDataType.VARCHAR || type == SQLDataType.CHAR) && asList(FIREBIRD).contains(family)) { - toSQLCast(context, dataType, getValueLength(), 0, 0); - } - - /* [pro] xx - xx xxxxxxx xxxx xxxx xxxxx xxxxxx xxx xx xxxx xx xxx xxxxxx xx xxxx xxxxxxxx - xxxx xx xxxxx xx xxxxxxxxxxxxxxxxxxxxxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxxxxxx xxxxxxxxx xx xx xxx - x - xx [/pro] */ - - // In all other cases, the bind variable can be cast normally - else { - toSQLCast(context, dataType, dataType.length(), dataType.precision(), dataType.scale()); - } - } - - private final int getValueLength() { - String string = (String) value; - if (string == null) { - return 1; + new DefaultBinding(getConverter(), getDataType().isLob(), getParamName()).sql(new DefaultBindingSQLContext(ctx.configuration(), (RenderContext) ctx, value)); + ctx.paramType(paramType); } else { - int length = string.length(); - // If non 7-bit ASCII characters are present, multiply the length by - // 4 to be sure that even UTF-32 collations will fit. But don't use - // larger numbers than Derby's upper limit 32672 - for (int i = 0; i < length; i++) { - if (string.charAt(i) > 127) { - return Math.min(32672, 4 * length); - } - } - - return Math.min(32672, length); - } - } - - private final void toSQLCast(RenderContext context, DataType type, int length, int precision, int scale) { - context.keyword("cast").sql("("); - toSQL(context, value, getConverter()); - context.sql(" ").keyword("as").sql(" ") - .sql(type.length(length).precision(precision, scale).getCastTypeName(context.configuration())) - .sql(")"); - } - - /** - * Get a bind variable, depending on value of - * {@link RenderContext#namedParams()} - */ - private final String getBindVariable(RenderContext context) { - if (context.paramType() == NAMED || context.paramType() == NAMED_OR_INLINED) { - int index = context.nextIndex(); - - if (StringUtils.isBlank(getParamName())) { - return ":" + index; - } - else { - return ":" + getName(); + // [#1302] Bind value only if it was not explicitly forced to be inlined + if (!isInline(ctx)) { + ctx.bindValue(value, this); } } - else { - return "?"; - } - } - - /** - * Inlining abstraction - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private final void toSQL(RenderContext context, Object val, Converter converter) { - SQLDialect family = context.configuration().dialect().family(); - - // [#650] [#3108] Check first, if we have a converter for the supplied type - Class type = converter.fromType(); - val = ((Converter) converter).to(val); - - if (isInline(context)) { - // [#2223] Some type-casts in this section may seem unnecessary, e.g. - // ((Boolean) val).toString(). They have been put in place to avoid - // accidental type confusions where type != val.getClass(), and thus - // SQL injection may occur - - if (val == null) { - context.keyword("null"); - } - else if (type == Boolean.class) { - - // [#1153] Some dialects don't support boolean literals TRUE and FALSE - if (asList(FIREBIRD, SQLITE).contains(family)) { - context.sql(((Boolean) val) ? "1" : "0"); - } - /* [pro] xx - xxxx xx xxxxxxx xx xxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxx xxxx x xxxxx x xxxxxxx - x - xx [/pro] */ - else { - context.keyword(((Boolean) val).toString()); - } - } - - // [#1154] Binary data cannot always be inlined - else if (type == byte[].class) { - byte[] binary = (byte[]) val; - - if (asList().contains(family)) { - context.sql("0x") - .sql(convertBytesToHex(binary)); - } - /* [pro] xx - xxxx xx xxxxxxx xx xxxx x - xxxxxxxxxxxxxxxxxxxxxxx - xxxxxxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xxxxxxxxxxx - x - xx [/pro] */ - else if (asList(DERBY, H2, HSQLDB, MARIADB, MYSQL, SQLITE).contains(family)) { - context.sql("X'") - .sql(convertBytesToHex(binary)) - .sql("'"); - } - else if (asList().contains(family)) { - context.keyword("hextoraw('") - .sql(convertBytesToHex(binary)) - .sql("')"); - } - else if (family == POSTGRES) { - context.sql("E'") - .sql(convertBytesToPostgresOctal(binary)) - .keyword("'::bytea"); - } - - // This default behaviour is used in debug logging for dialects - // that do not support inlining binary data - else { - context.sql("X'") - .sql(convertBytesToHex(binary)) - .sql("'"); - } - } - - // Interval extends Number, so let Interval come first! - else if (Interval.class.isAssignableFrom(type)) { - context.sql("'") - .sql(escape(val, context)) - .sql("'"); - } - - else if (Number.class.isAssignableFrom(type)) { - context.sql(((Number) val).toString()); - } - - // [#1156] Date/Time data types should be inlined using JDBC - // escape syntax - else if (type == Date.class) { - - // The SQLite JDBC driver does not implement the escape syntax - // [#1253] SQL Server and Sybase do not implement date literals - if (asList(SQLITE).contains(family)) { - context.sql("'").sql(escape(val, context)).sql("'"); - } - - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx - x - - xxxx xx xxxxxxx xx xxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xx xxxxxx - x - xx [/pro] */ - - // [#1253] Derby doesn't support the standard literal - else if (family == DERBY) { - context.keyword("date('").sql(escape(val, context)).sql("')"); - } - - // [#3648] Circumvent a MySQL bug related to date literals - else if (family == MYSQL) { - context.keyword("{d '").sql(escape(val, context)).sql("'}"); - } - - // Most dialects implement SQL standard date literals - else { - context.keyword("date '").sql(escape(val, context)).sql("'"); - } - } - else if (type == Timestamp.class) { - - // The SQLite JDBC driver does not implement the escape syntax - // [#1253] SQL Server and Sybase do not implement timestamp literals - if (asList(SQLITE).contains(family)) { - context.sql("'").sql(escape(val, context)).sql("'"); - } - - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx - x - - xxxx xx xxxxxxx xx xxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xx xxxxxxxxxxx - x - xx [/pro] */ - - // [#1253] Derby doesn't support the standard literal - else if (family == DERBY) { - context.keyword("timestamp('").sql(escape(val, context)).sql("')"); - } - - // CUBRID timestamps have no fractional seconds - else if (family == CUBRID) { - context.keyword("datetime '").sql(escape(val, context)).sql("'"); - } - - // [#3648] Circumvent a MySQL bug related to date literals - else if (family == MYSQL) { - context.keyword("{ts '").sql(escape(val, context)).sql("'}"); - } - - // Most dialects implement SQL standard timestamp literals - else { - context.keyword("timestamp '").sql(escape(val, context)).sql("'"); - } - } - else if (type == Time.class) { - - // The SQLite JDBC driver does not implement the escape syntax - // [#1253] SQL Server and Sybase do not implement time literals - if (asList(SQLITE).contains(family)) { - context.sql("'").sql(new SimpleDateFormat("HH:mm:ss").format((Time) val)).sql("'"); - } - - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx - x - - xxxx xx xxxxxxx xx xxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xx xxxxxxxxx - x - xx [/pro] */ - - // [#1253] Derby doesn't support the standard literal - else if (family == DERBY) { - context.keyword("time").sql("('").sql(escape(val, context)).sql("')"); - } - - // [#3648] Circumvent a MySQL bug related to date literals - else if (family == MYSQL) { - context.keyword("{t '").sql(escape(val, context)).sql("'}"); - } - /* [pro] xx - xx xxxxxxx xxxxxx xxxxxxx xxxx xxxx xxxxxxxx - xxxx xx xxxxxxx xx xxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx - x - - xx [/pro] */ - // Most dialects implement SQL standard time literals - else { - context.keyword("time").sql(" '").sql(escape(val, context)).sql("'"); - } - } - else if (type.isArray()) { - String separator = ""; - - // H2 renders arrays as rows - if (family == H2) { - context.sql("("); - - for (Object o : ((Object[]) val)) { - context.sql(separator); - toSQL(context, o, new IdentityConverter(type.getComponentType())); - separator = ", "; - } - - context.sql(")"); - } - - // By default, render HSQLDB / POSTGRES syntax - else { - context.keyword("ARRAY"); - context.sql("["); - - for (Object o : ((Object[]) val)) { - context.sql(separator); - toSQL(context, o, new IdentityConverter(type.getComponentType())); - separator = ", "; - } - - context.sql("]"); - - // [#3214] Some PostgreSQL array type literals need explicit casting - if (family == POSTGRES && EnumType.class.isAssignableFrom(type.getComponentType())) { - context.sql("::") - .keyword(DefaultDataType.getDataType(family, type).getCastTypeName(context.configuration())); - } - } - } - /* [pro] xx - xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx xxxxxx - x - xx [/pro] */ - else if (EnumType.class.isAssignableFrom(type)) { - String literal = ((EnumType) val).getLiteral(); - - if (literal == null) { - toSQL(context, val, new IdentityConverter(String.class)); - } - else { - toSQL(context, val, new IdentityConverter(String.class)); - } - } - else if (UDTRecord.class.isAssignableFrom(type)) { - context.sql("[UDT]"); - } - - // Known fall-through types: - // - Blob, Clob (both not supported by jOOQ) - // - String - // - UUID - else { - context.sql("'") - .sql(escape(val, context), true) - .sql("'"); - } - } - - // In Postgres, some additional casting must be done in some cases... - else if (family == SQLDialect.POSTGRES) { - - // Postgres needs explicit casting for array types - if (type.isArray() && byte[].class != type) { - context.sql(getBindVariable(context)); - context.sql("::"); - context.keyword(DefaultDataType.getDataType(family, type).getCastTypeName(context.configuration())); - } - - // ... and also for enum types - else if (EnumType.class.isAssignableFrom(type)) { - context.sql(getBindVariable(context)); - - // [#968] Don't cast "synthetic" enum types (note, val can be null!) - EnumType e = (EnumType) type.getEnumConstants()[0]; - Schema schema = e.getSchema(); - - if (schema != null) { - context.sql("::"); - - schema = using(context.configuration()).map(schema); - if (schema != null && TRUE.equals(context.configuration().settings().isRenderSchema())) { - context.visit(schema); - context.sql("."); - } - - context.visit(name(e.getName())); - } - } - - else { - context.sql(getBindVariable(context)); - } - } - - else { - context.sql(getBindVariable(context)); - } - } - - /** - * Escape a string literal by replacing ' by '', and possibly also backslashes. - */ - private final String escape(Object val, Context context) { - String result = val.toString(); - - if (needsBackslashEscaping(context.configuration())) - result = result.replace("\\", "\\\\"); - - return result.replace("'", "''"); - } - - final void bind0(BindContext context) { - - // [#1302] Bind value only if it was not explicitly forced to be inlined - if (!isInline(context)) { - context.bindValue(value, this); - } - } - - // ------------------------------------------------------------------------ - // XXX: Param API - // ------------------------------------------------------------------------ - - /** - * Convert a byte array to a hex encoded string. - * - * @param value the byte array - * @return the hex encoded string - */ - private static final String convertBytesToHex(byte[] value) { - return convertBytesToHex(value, value.length); - } - - /** - * Convert a byte array to a hex encoded string. - * - * @param value the byte array - * @param len the number of bytes to encode - * @return the hex encoded string - */ - private static final String convertBytesToHex(byte[] value, int len) { - char[] buff = new char[len + len]; - char[] hex = HEX; - for (int i = 0; i < len; i++) { - int c = value[i] & 0xff; - buff[i + i] = hex[c >> 4]; - buff[i + i + 1] = hex[c & 0xf]; - } - return new String(buff); - } - - /** - * Postgres uses octals instead of hex encoding - */ - private static final String convertBytesToPostgresOctal(byte[] binary) { - StringBuilder sb = new StringBuilder(); - - for (byte b : binary) { - sb.append("\\\\"); - sb.append(leftPad(toOctalString(b), 3, '0')); - } - - return sb.toString(); } }