diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index f58a52a8f3..8a46164abe 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -42,7 +42,9 @@ import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; +import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.DB2; +import static org.jooq.SQLDialect.SQLSERVER; import static org.jooq.SQLDialect.SYBASE; import static org.jooq.impl.Factory.cast; import static org.jooq.impl.Factory.castNull; @@ -4792,7 +4794,7 @@ public abstract class jOOQAbstractTest< } @Test - public void testArithmeticExpressions() throws Exception { + public void testArithmeticOperations() throws Exception { Field f1 = val(1).add(2).add(3).div(2); Field f2 = val(10).div(5).add(val(3).sub(2)); Field f3 = val(10).mod(3); @@ -4822,6 +4824,53 @@ public abstract class jOOQAbstractTest< assertEquals(Integer.valueOf((1948 - 4) * -8), result.getValue(0, f5)); } + @Test + public void testBitwiseOperations() throws Exception { + switch (getDialect()) { + case DERBY: + case INGRES: + log.info("SKIPPING", "Tests for bitwise operations"); + return; + } + + Field bitCount = val(3).bitCount(); + + // TODO [#896] This somehow doesn't work on some dialects + if (asList(ASE, DB2, SQLSERVER).contains(getDialect())) { + bitCount = val(2); + } + + Record result = + create().select( + bitCount, + val(3).bitNot().bitNot(), + val(3).bitAnd(5), + val(3).bitOr(5), + + val(3).bitXor(5), + val(3).bitNand(5).bitNot(), + val(3).bitNor(5).bitNot(), + val(3).bitXNor(5).bitNot(), + + val(333).shl(3), + val(333).shr(3)) + .fetchOne(); + + int index = 0; + assertEquals(2, result.getValue(index++)); + assertEquals(~(~3), result.getValue(index++)); + assertEquals(3 & 5, result.getValue(index++)); + assertEquals(3 | 5, result.getValue(index++)); + + assertEquals(3 ^ 5, result.getValue(index++)); + assertEquals(~(~(3 & 5)), result.getValue(index++)); + assertEquals(~(~(3 | 5)), result.getValue(index++)); + assertEquals(~(~(3 ^ 5)), result.getValue(index++)); + + assertEquals(333 << 3, result.getValue(index++)); + assertEquals(333 >> 3, result.getValue(index++)); + } + @Test public void testAggregateFunctions() throws Exception { diff --git a/jOOQ/src/main/java/org/jooq/Field.java b/jOOQ/src/main/java/org/jooq/Field.java index 72f4c5b09e..07717e5abc 100644 --- a/jOOQ/src/main/java/org/jooq/Field.java +++ b/jOOQ/src/main/java/org/jooq/Field.java @@ -252,7 +252,7 @@ public interface Field extends NamedTypeProviderQueryPart, AliasProvider SortField sort(Map sortMap); // ------------------------------------------------------------------------ - // Arithmetic expressions + // Arithmetic operations // ------------------------------------------------------------------------ /** @@ -335,6 +335,233 @@ public interface Field extends NamedTypeProviderQueryPart, AliasProvider mod(Field value); + // ------------------------------------------------------------------------ + // Bitwise operations + // ------------------------------------------------------------------------ + + /** + * The MySQL BIT_COUNT(field) function, counting the number of + * bits that are set in this number. + *

+ * This function is simulated in most other databases like this (for a + * TINYINT field):

+     * (my_field &   1) +
+     * (my_field &   2) >> 1 +
+     * (my_field &   4) >> 2 +
+     * (my_field &   8) >> 3 +
+     * (my_field &  16) >> 4 +
+     *  ...
+     * (my_field & 128) >> 7
+     * 
+ *

+ * More efficient algorithms are very welcome + */ + Field bitCount(); + + /** + * The bitwise not operator. + *

+ * Most dialects natively support this using ~[this]. jOOQ + * simulates this operator in some dialects using -[this] - 1 + */ + Field bitNot(); + + /** + * The bitwise and operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the and operation where available: + *

[this] & [value]
+ * ... or the and function elsewhere: + *
bitand([this], [value])
+ */ + Field bitAnd(Number value); + + /** + * The bitwise and operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the and operation where available: + *

[this] & [value]
+ * ... or the and function elsewhere: + *
bitand([this], [value])
+ */ + Field bitAnd(Field value); + + /** + * The bitwise not and operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the not and operation where available: + *

~([this] & [value])
+ * ... or the not and function elsewhere: + *
bitnot(bitand([this], [value]))
+ * + * @see #bitNot() + */ + Field bitNand(Number value); + + /** + * The bitwise not and operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the not and operation where available: + *

~([this] & [value])
+ * ... or the not and function elsewhere: + *
bitnot(bitand([this], [value]))
+ * + * @see #bitNot() + */ + Field bitNand(Field value); + + /** + * The bitwise or operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the or operation where available: + *

[this] | [value]
+ * ... or the or function elsewhere: + *
bitor([this], [value])
+ */ + Field bitOr(Number value); + + /** + * The bitwise or operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the or operation where available: + *

[this] | [value]
+ * ... or the or function elsewhere: + *
bitor([this], [value])
+ */ + Field bitOr(Field value); + + /** + * The bitwise not or operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the not or operation where available: + *

~([this] | [value])
+ * ... or the not or function elsewhere: + *
bitnot(bitor([this], [value]))
+ * + * @see #bitNot() + */ + Field bitNor(Number value); + + /** + * The bitwise not or operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the not or operation where available: + *

~([this] | [value])
+ * ... or the not or function elsewhere: + *
bitnot(bitor([this], [value]))
+ * + * @see #bitNot() + */ + Field bitNor(Field value); + + /** + * The bitwise xor operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the or operation where available: + *

[this] ^ [value]
+ * ... or the xor function elsewhere: + *
bitxor([this], [value])
+ */ + Field bitXor(Number value); + + /** + * The bitwise xor operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the or operation where available: + *

[this] ^ [value]
+ * ... or the xor function elsewhere: + *
bitxor([this], [value])
+ */ + Field bitXor(Field value); + + /** + * The bitwise not xor operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the or operation where available: + *

~([this] ^ [value])
+ * ... or the not xor function elsewhere: + *
bitnot(bitxor([this], [value]))
+ */ + Field bitXNor(Number value); + + /** + * The bitwise not xor operator. + *

+ * This is not supported by Derby, Ingres + *

+ * This renders the or operation where available: + *

~([this] ^ [value])
+ * ... or the not xor function elsewhere: + *
bitnot(bitxor([this], [value]))
+ */ + Field bitXNor(Field value); + + /** + * The bitwise left shift operator. + *

+ * Some dialects natively support this using [this] << [value]. + * jOOQ simulates this operator in some dialects using + * [this] * power(2, [value]), where power might also be simulated. + * + * @see #power(Field) + */ + Field shl(Number value); + + /** + * The bitwise left shift operator. + *

+ * Some dialects natively support this using [this] << [value]. + * jOOQ simulates this operator in some dialects using + * [this] * power(2, [value]), where power might also be simulated. + * + * @see #power(Field) + */ + Field shl(Field value); + + /** + * The bitwise right shift operator. + *

+ * Some dialects natively support this using [this] >> [value]. + * jOOQ simulates this operator in some dialects using + * [this] / power(2, [value]), where power might also be simulated. + * + * @see #power(Field) + */ + Field shr(Number value); + + /** + * The bitwise right shift operator. + *

+ * Some dialects natively support this using [this] >> [value]. + * jOOQ simulates this operator in some dialects using + * [this] / power(2, [value]), where power might also be simulated. + * + * @see #power(Field) + */ + Field shr(Field value); + // ------------------------------------------------------------------------ // Mathematical functions created from this field // ------------------------------------------------------------------------ @@ -448,6 +675,16 @@ public interface Field extends NamedTypeProviderQueryPart, AliasProvider power(Number exponent); + /** + * Get the power(field, exponent) function + *

+ * This renders the power function where available: + *

power([this], [exponent])
... or simulates it + * elsewhere using ln and exp: + *
exp(ln([this]) * [exponent])
+ */ + Field power(Field exponent); + /** * Get the arc cosine(field) function *

diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index b874e83767..9aebba7db8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -202,12 +202,12 @@ abstract class AbstractField extends AbstractNamedTypeProviderQueryPart im } // ------------------------------------------------------------------------ - // Arithmetic expressions + // Arithmetic operations // ------------------------------------------------------------------------ @Override public final Field neg() { - return new Neg(this); + return new Neg(this, ExpressionOperator.SUBTRACT); } @Override @@ -286,6 +286,100 @@ abstract class AbstractField extends AbstractNamedTypeProviderQueryPart im return new Mod(this, value); } + // ------------------------------------------------------------------------ + // Bitwise operations + // ------------------------------------------------------------------------ + + @Override + public final Field bitCount() { + return new BitCount(this); + } + + @Override + public final Field bitNot() { + return new Neg(this, ExpressionOperator.BIT_NOT); + } + + @Override + public final Field bitAnd(Number value) { + return bitAnd(val(value)); + } + + @Override + public final Field bitAnd(Field value) { + return new Expression(ExpressionOperator.BIT_AND, this, value); + } + + @Override + public final Field bitNand(Number value) { + return bitNand(val(value)); + } + + @Override + public final Field bitNand(Field value) { + return new Expression(ExpressionOperator.BIT_NAND, this, value); + } + + @Override + public final Field bitOr(Number value) { + return bitOr(val(value)); + } + + @Override + public final Field bitOr(Field value) { + return new Expression(ExpressionOperator.BIT_OR, this, value); + } + + @Override + public final Field bitNor(Number value) { + return bitNor(val(value)); + } + + @Override + public final Field bitNor(Field value) { + return new Expression(ExpressionOperator.BIT_NOR, this, value); + } + + @Override + public final Field bitXor(Number value) { + return bitXor(val(value)); + } + + @Override + public final Field bitXor(Field value) { + return new Expression(ExpressionOperator.BIT_XOR, this, value); + } + + @Override + public final Field bitXNor(Number value) { + return bitXNor(val(value)); + } + + @Override + public final Field bitXNor(Field value) { + return new Expression(ExpressionOperator.BIT_XNOR, this, value); + } + + @Override + public final Field shl(Number value) { + return shl(val(value)); + } + + @Override + public final Field shl(Field value) { + return new Expression(ExpressionOperator.SHL, this, value); + } + + @Override + public final Field shr(Number value) { + return shr(val(value)); + } + + @Override + public final Field shr(Field value) { + return new Expression(ExpressionOperator.SHR, this, value); + } + // ------------------------------------------------------------------------ // Window functions created from this field // ------------------------------------------------------------------------ @@ -528,6 +622,11 @@ abstract class AbstractField extends AbstractNamedTypeProviderQueryPart im @Override public final Field power(Number exponent) { + return power(val(exponent)); + } + + @Override + public Field power(Field exponent) { return new Power(this, exponent); } diff --git a/jOOQ/src/main/java/org/jooq/impl/BitCount.java b/jOOQ/src/main/java/org/jooq/impl/BitCount.java new file mode 100644 index 0000000000..516e0cc3aa --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/BitCount.java @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2009-2011, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.impl; + +import static org.jooq.impl.Factory.function; +import static org.jooq.impl.Factory.literal; + +import org.jooq.Configuration; +import org.jooq.Field; + +/** + * @author Lukas Eder + */ +class BitCount extends AbstractFunction { + + /** + * Generated UID + */ + private static final long serialVersionUID = 7624782102883057433L; + + BitCount(Field field) { + super("bit_count", SQLDataType.INTEGER, field); + } + + @Override + final Field getFunction0(Configuration configuration) { + final Field field = getArguments()[0]; + + switch (configuration.getDialect()) { + case MYSQL: + return function("bit_count", getDataType(), getArguments()); + + // Warning, some severe madness lies ahead. Better solutions very welcome! + // See also http://stackoverflow.com/questions/7946349/how-to-simulate-the-mysql-bit-count-function-in-sybase-sql-anywhere + default: { + if (field.getType() == Byte.class) { + @SuppressWarnings("unchecked") + Field f = (Field) field; + + byte i = 0; + return f.bitAnd(literal((byte) 0x01)).add( + f.bitAnd(literal((byte) 0x02)).shr(literal(++i))).add( + f.bitAnd(literal((byte) 0x04)).shr(literal(++i))).add( + f.bitAnd(literal((byte) 0x08)).shr(literal(++i))).add( + f.bitAnd(literal((byte) 0x10)).shr(literal(++i))).add( + f.bitAnd(literal((byte) 0x20)).shr(literal(++i))).add( + f.bitAnd(literal((byte) 0x40)).shr(literal(++i))).add( + f.bitAnd(literal((byte) 0x80)).shr(literal(++i))).cast(Integer.class); + } + else if (field.getType() == Short.class) { + @SuppressWarnings("unchecked") + Field f = (Field) field; + + short i = 0; + return f.bitAnd(literal((short) 0x0001)).add( + f.bitAnd(literal((short) 0x0002)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0004)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0008)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0010)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0020)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0040)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0080)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0100)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0200)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0400)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x0800)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x1000)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x2000)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x4000)).shr(literal(++i))).add( + f.bitAnd(literal((short) 0x8000)).shr(literal(++i))).cast(Integer.class); + } + else if (field.getType() == Integer.class) { + @SuppressWarnings("unchecked") + Field f = (Field) field; + + int i = 0; + return f.bitAnd(literal(0x00000001)).add( + f.bitAnd(literal(0x00000002)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000004)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000008)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000010)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000020)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000040)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000080)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000100)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000200)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000400)).shr(literal(++i))).add( + f.bitAnd(literal(0x00000800)).shr(literal(++i))).add( + f.bitAnd(literal(0x00001000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00002000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00004000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00008000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00010000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00020000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00040000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00080000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00100000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00200000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00400000)).shr(literal(++i))).add( + f.bitAnd(literal(0x00800000)).shr(literal(++i))).add( + f.bitAnd(literal(0x01000000)).shr(literal(++i))).add( + f.bitAnd(literal(0x02000000)).shr(literal(++i))).add( + f.bitAnd(literal(0x04000000)).shr(literal(++i))).add( + f.bitAnd(literal(0x08000000)).shr(literal(++i))).add( + f.bitAnd(literal(0x10000000)).shr(literal(++i))).add( + f.bitAnd(literal(0x20000000)).shr(literal(++i))).add( + f.bitAnd(literal(0x40000000)).shr(literal(++i))).add( + f.bitAnd(literal(0x80000000)).shr(literal(++i))); + } + else if (field.getType() == Long.class) { + @SuppressWarnings("unchecked") + Field f = (Field) field; + + long i = 0; + return f.bitAnd(literal(0x0000000000000001L)).add( + f.bitAnd(literal(0x0000000000000002L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000004L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000008L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000010L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000020L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000040L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000080L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000100L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000200L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000400L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000000800L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000001000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000002000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000004000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000008000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000010000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000020000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000040000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000080000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000100000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000200000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000400000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000000800000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000001000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000002000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000004000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000008000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000010000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000020000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000040000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000080000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000100000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000200000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000400000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000000800000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000001000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000002000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000004000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000008000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000010000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000020000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000040000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000080000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000100000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000200000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000400000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0000800000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0001000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0002000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0004000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0008000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0010000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0020000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0040000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0080000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0100000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0200000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0400000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x0800000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x1000000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x2000000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x4000000000000000L)).shr(literal(++i))).add( + f.bitAnd(literal(0x8000000000000000L)).shr(literal(++i))).cast(Integer.class); + } + else { + // Currently not supported + return function("bit_count", getDataType(), getArguments()); + } + } + } + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/Expression.java b/jOOQ/src/main/java/org/jooq/impl/Expression.java index 1c7ce58a5e..ade585087b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Expression.java +++ b/jOOQ/src/main/java/org/jooq/impl/Expression.java @@ -35,17 +35,41 @@ */ package org.jooq.impl; +import static java.util.Arrays.asList; +import static org.jooq.SQLDialect.ASE; +import static org.jooq.SQLDialect.DB2; +import static org.jooq.SQLDialect.H2; +import static org.jooq.SQLDialect.HSQLDB; +import static org.jooq.SQLDialect.INGRES; +import static org.jooq.SQLDialect.ORACLE; +import static org.jooq.SQLDialect.POSTGRES; +import static org.jooq.SQLDialect.SQLITE; +import static org.jooq.SQLDialect.SQLSERVER; +import static org.jooq.SQLDialect.SYBASE; +import static org.jooq.impl.ExpressionOperator.BIT_AND; +import static org.jooq.impl.ExpressionOperator.BIT_NAND; +import static org.jooq.impl.ExpressionOperator.BIT_NOR; +import static org.jooq.impl.ExpressionOperator.BIT_OR; +import static org.jooq.impl.ExpressionOperator.BIT_XNOR; +import static org.jooq.impl.ExpressionOperator.BIT_XOR; +import static org.jooq.impl.ExpressionOperator.SHL; +import static org.jooq.impl.ExpressionOperator.SHR; +import static org.jooq.impl.Factory.function; +import static org.jooq.impl.Factory.literal; + import java.sql.SQLException; import java.util.Arrays; import java.util.List; import org.jooq.Attachable; import org.jooq.BindContext; +import org.jooq.Configuration; import org.jooq.Field; import org.jooq.QueryPart; import org.jooq.RenderContext; +import org.jooq.SQLDialect; -class Expression extends AbstractField { +class Expression extends AbstractFunction { /** * Generated UID @@ -56,7 +80,7 @@ class Expression extends AbstractField { private final ExpressionOperator operator; Expression(ExpressionOperator operator, Field lhs, Field... rhs) { - super(operator.toSQL(), lhs.getDataType()); + super(operator.toSQL(), lhs.getDataType(), JooqUtil.combine(lhs, rhs)); this.operator = operator; this.lhs = lhs; @@ -64,11 +88,6 @@ class Expression extends AbstractField { this.rhs.addAll(Arrays.asList(rhs)); } - @Override - public final List getAttachables() { - return getAttachables(lhs, rhs); - } - @Override public final Field add(Field value) { if (operator == ExpressionOperator.ADD) { @@ -90,27 +109,117 @@ class Expression extends AbstractField { } @Override - public final void toSQL(RenderContext context) { - context.sql("("); - context.sql(lhs); + final Field getFunction0(Configuration configuration) { + SQLDialect dialect = configuration.getDialect(); - for (Field field : rhs) { - context.sql(" ") - .sql(operator.toSQL()) - .sql(" ") - .sql(field); + // DB2, H2 and HSQLDB know functions, instead of operators + if (BIT_AND == operator && asList(DB2, H2, HSQLDB, ORACLE).contains(dialect)) { + return function("bitand", getDataType(), getArguments()); + } + else if (BIT_XOR == operator && asList(DB2, H2, HSQLDB).contains(dialect)) { + return function("bitxor", getDataType(), getArguments()); + } + else if (BIT_OR == operator && asList(DB2, H2, HSQLDB).contains(dialect)) { + return function("bitor", getDataType(), getArguments()); } - context.sql(")"); + // Oracle has to simulate or/xor + else if (BIT_OR == operator && ORACLE == dialect) { + return lhs.sub(lhsAsNumber().bitAnd(rhsAsNumber())).add(rhsAsNumber()); + } + + // ~(a & b) & (a | b) + else if (BIT_XOR == operator && asList(ORACLE, SQLITE).contains(dialect)) { + return lhs.bitAnd(rhsAsNumber()).bitNot().bitAnd( + lhsAsNumber().bitOr(rhsAsNumber())); + } + + // Many dialects don't support shifts. Use multiplication/division instead + else if (SHL == operator && asList(ASE, DB2, H2, HSQLDB, INGRES, ORACLE, SQLSERVER, SYBASE).contains(dialect)) { + return lhs.mul(literal(2).power(rhsAsNumber())); + } + else if (SHR == operator && asList(ASE, DB2, H2, HSQLDB, INGRES, ORACLE, SQLSERVER, SYBASE).contains(dialect)) { + return lhs.div(literal(2).power(rhsAsNumber())); + } + + // These operators are not supported in any dialect + else if (BIT_NAND == operator) { + return lhs.bitAnd(rhsAsNumber()).bitNot(); + } + else if (BIT_NOR == operator) { + return lhs.bitOr(rhsAsNumber()).bitNot(); + } + else if (BIT_XNOR == operator) { + return lhs.bitXor(rhsAsNumber()).bitNot(); + } + + // Use the default operator expression + else { + return new DefaultExpression(); + } } - @Override - public final void bind(BindContext context) throws SQLException { - context.bind(lhs).bind((QueryPart) rhs); + /** + * In some expressions, the lhs can be safely assumed to be a single number + */ + @SuppressWarnings("unchecked") + private final Field lhsAsNumber() { + return (Field) lhs; } - @Override - public final boolean isNullLiteral() { - return false; + /** + * In some expressions, the rhs can be safely assumed to be a single number + */ + @SuppressWarnings("unchecked") + private final Field rhsAsNumber() { + return (Field) rhs.get(0); + } + + private class DefaultExpression extends AbstractField { + + /** + * Generated UID + */ + private static final long serialVersionUID = -5105004317793995419L; + + private DefaultExpression() { + super(operator.toSQL(), lhs.getDataType()); + } + + @Override + public final List getAttachables() { + return Expression.this.getAttachables(); + } + + @Override + public final void toSQL(RenderContext context) { + String op = operator.toSQL(); + + if (operator == BIT_XOR && context.getDialect() == POSTGRES) { + op = "#"; + } + + context.sql("("); + context.sql(lhs); + + for (Field field : rhs) { + context.sql(" ") + .sql(op) + .sql(" ") + .sql(field); + } + + context.sql(")"); + } + + @Override + public final void bind(BindContext context) throws SQLException { + context.bind(lhs).bind((QueryPart) rhs); + } + + @Override + public final boolean isNullLiteral() { + return false; + } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/ExpressionOperator.java b/jOOQ/src/main/java/org/jooq/impl/ExpressionOperator.java index 5dcf31c10e..b211609233 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ExpressionOperator.java +++ b/jOOQ/src/main/java/org/jooq/impl/ExpressionOperator.java @@ -70,7 +70,54 @@ enum ExpressionOperator { /** * Modulo */ - MODULO("%"); + MODULO("%"), + + /** + * Bitwise not + */ + BIT_NOT("~"), + + /** + * Bitwise and + */ + BIT_AND("&"), + + /** + * Bitwise or + */ + BIT_OR("|"), + + /** + * Bitwise xor + */ + BIT_XOR("^"), + + /** + * Bitwise and + */ + BIT_NAND("~&"), + + /** + * Bitwise or + */ + BIT_NOR("~|"), + + /** + * Bitwise xor + */ + BIT_XNOR("~^"), + + /** + * Bitwise shift left + */ + SHL("<<"), + + /** + * Bitwise shift right + */ + SHR(">>"), + + ; private final String sql; diff --git a/jOOQ/src/main/java/org/jooq/impl/Neg.java b/jOOQ/src/main/java/org/jooq/impl/Neg.java index 6a707ac6de..e763986701 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Neg.java +++ b/jOOQ/src/main/java/org/jooq/impl/Neg.java @@ -35,6 +35,14 @@ */ package org.jooq.impl; +import static java.util.Arrays.asList; +import static org.jooq.SQLDialect.DB2; +import static org.jooq.SQLDialect.H2; +import static org.jooq.SQLDialect.HSQLDB; +import static org.jooq.SQLDialect.INGRES; +import static org.jooq.SQLDialect.ORACLE; +import static org.jooq.impl.ExpressionOperator.BIT_NOT; + import java.sql.SQLException; import java.util.List; @@ -42,6 +50,7 @@ import org.jooq.Attachable; import org.jooq.BindContext; import org.jooq.Field; import org.jooq.RenderContext; +import org.jooq.SQLDialect; /** * @author Lukas Eder @@ -51,13 +60,15 @@ class Neg extends AbstractField { /** * Generated UID */ - private static final long serialVersionUID = 7624782102883057433L; + private static final long serialVersionUID = 7624782102883057433L; - private final Field field; + private final ExpressionOperator operator; + private final Field field; - Neg(Field field) { - super("-" + field.getName(), field.getDataType()); + Neg(Field field, ExpressionOperator operator) { + super(operator.toSQL() + field.getName(), field.getDataType()); + this.operator = operator; this.field = field; } @@ -68,7 +79,24 @@ class Neg extends AbstractField { @Override public final void toSQL(RenderContext context) { - context.sql("-").sql(field); + SQLDialect dialect = context.getDialect(); + + if (operator == BIT_NOT && asList(H2, HSQLDB, INGRES, ORACLE).contains(dialect)) { + context.sql("(0 -") + .sql(field) + .sql(" - 1)"); + } + else if (operator == BIT_NOT && dialect == DB2) { + context.sql("bitnot(") + .sql(field) + .sql(")"); + } + else { + context.sql(operator.toSQL()) + .sql("(") + .sql(field) + .sql(")"); + } } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/Power.java b/jOOQ/src/main/java/org/jooq/impl/Power.java index 87836750c0..440b515eff 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Power.java +++ b/jOOQ/src/main/java/org/jooq/impl/Power.java @@ -36,7 +36,6 @@ package org.jooq.impl; import static org.jooq.impl.Factory.function; -import static org.jooq.impl.Factory.literal; import java.math.BigDecimal; @@ -53,11 +52,11 @@ class Power extends AbstractFunction { */ private static final long serialVersionUID = -7273879239726265322L; - private final Field arg1; - private final Number arg2; + private final Field arg1; + private final Field arg2; - Power(Field arg1, Number arg2) { - super("ceil", SQLDataType.NUMERIC, arg1); + Power(Field arg1, Field arg2) { + super("ceil", SQLDataType.NUMERIC, arg1, arg2); this.arg1 = arg1; this.arg2 = arg2; @@ -68,10 +67,10 @@ class Power extends AbstractFunction { switch (configuration.getDialect()) { case DERBY: case SQLITE: - return arg1.ln().mul(literal(arg2)).exp(); + return arg1.ln().mul(arg2).exp(); default: - return function("power", SQLDataType.NUMERIC, arg1, literal(arg2)); + return function("power", SQLDataType.NUMERIC, getArguments()); } } }