diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 1dfaba3..e2f1ee0 100755 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -1518,6 +1518,112 @@ public function orWhereJsonDoesntOverlap(string $column, mixed $value): static return $this->whereJsonDoesntOverlap($column, $value, 'or'); } + /** + * Add an "where Bit Functions and Operators" clause to the query. + */ + public function whereBit(string $key, mixed $operator = 'and', mixed $value = null, string $boolean = 'and', bool $not = false): static + { + $type = $not ? '!=' : '='; + [$value, $operator] = $this->prepareValueAndOperator($value, $operator, func_num_args() === 2); + $operator = match ($operator) { + '|', 'or' => '|', + '^', 'xor' => '^', + default => '&' + }; + return $this->whereRaw(sprintf('%s %s ? %s ?', $key, $operator, $type), [$value, $value], $boolean); + } + + /** + * Add an "where Bit Not Functions and Operators" clause to the query. + */ + public function whereBitNot(string $key, mixed $operator = 'and', mixed $value = null, string $boolean = 'and'): static + { + [$value, $operator] = $this->prepareValueAndOperator($value, $operator, func_num_args() === 2); + return $this->whereBit($key, $operator, $value, $boolean, true); + } + + /** + * Add an "or where Bit Functions and Operators" clause to the query. + */ + public function orWhereBit(string $key, mixed $operator = 'and', mixed $value = null, bool $not = false): static + { + [$value, $operator] = $this->prepareValueAndOperator($value, $operator, func_num_args() === 2); + return $this->whereBit($key, $operator, $value, 'or', $not); + } + + /** + * Add an "or where Bit Not Functions and Operators" clause to the query. + */ + public function orWhereBitNot(string $key, mixed $operator = 'and', mixed $value = null): static + { + [$value, $operator] = $this->prepareValueAndOperator($value, $operator, func_num_args() === 2); + return $this->orWhereBit($key, $operator, $value, true); + } + + /** + * Add an "where Bit Or Functions and Operators" clause to the query. + */ + public function whereBitOr(string $key, mixed $value = null, bool $not = false): static + { + return $this->whereBit($key, 'or', $value, 'and', $not); + } + + /** + * Add an "where Bit Or Not Functions and Operators" clause to the query. + */ + public function whereBitOrNot(string $key, mixed $value = null): static + { + return $this->orWhereBit($key, 'or', $value, true); + } + + /** + * Add an "or where Bit Or Functions and Operators" clause to the query. + */ + public function orWhereBitOr(string $key, mixed $value = null, bool $not = false): static + { + return $this->orWhereBit($key, 'or', $value, $not); + } + + /** + * Add an "or where Bit Or Functions and Operators" clause to the query. + */ + public function orWhereBitOrNot(string $key, mixed $value = null): static + { + return $this->orWhereBitOr($key, $value, true); + } + + /** + * Add an "where Bit Xor Functions and Operators" clause to the query. + */ + public function whereBitXor(string $key, mixed $value = null, bool $not = false): static + { + return $this->whereBit($key, 'xor', $value, 'and', $not); + } + + /** + * Add an "where Bit Xor Not Functions and Operators" clause to the query. + */ + public function whereBitXorNot(string $key, mixed $value = null): static + { + return $this->whereBitXor($key, $value, true); + } + + /** + * Add an "or where Bit Xor Functions and Operators" clause to the query. + */ + public function orWhereBitXor(string $key, mixed $value = null, bool $not = false): static + { + return $this->orWhereBit($key, 'xor', $value, $not); + } + + /** + * Add an "or where Bit Xor Not Functions and Operators" clause to the query. + */ + public function orWhereBitXorNot(string $key, mixed $value = null): static + { + return $this->orWhereBitXor($key, $value, true); + } + /** * Add a "where JSON length" clause to the query. * diff --git a/tests/ModelRealBuilderTest.php b/tests/ModelRealBuilderTest.php index d23da6a..6069064 100644 --- a/tests/ModelRealBuilderTest.php +++ b/tests/ModelRealBuilderTest.php @@ -204,6 +204,24 @@ public function testForPageAfterId() } } + public function testUserWhereBit() + { + $this->getContainer(); + + $query = User::query()->whereBit('gender', 1); + $res = $query->get(); + $this->assertTrue($res->count() > 0); + + $sqls = [ + ['select * from `user` where gender & ? = ?', [1, 1]], + ]; + while ($event = $this->channel->pop(0.001)) { + if ($event instanceof QueryExecuted) { + $this->assertSame([$event->sql, $event->bindings], array_shift($sqls)); + } + } + } + public function testForceIndexes() { $this->getContainer(); diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index c5fb12a..0a1d416 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -3006,6 +3006,60 @@ public function testFromRawWithWhereOnTheMainQuery() $this->assertEquals(['1520652582'], $builder->getBindings()); } + public function testBitWheres() + { + $type = 16; + $flags = 32; + $builder = $this->getBuilder(); + $clone = $builder->clone(); + + $builder->select('*')->from('users')->whereBit('type', $type); + $this->assertEquals('select * from "users" where type & ? = ?', $builder->toSql()); + $builder->select('*')->from('users')->orWhereBit('flags', $flags); + $this->assertEquals('select * from "users" where type & ? = ? or flags & ? = ?', $builder->toSql()); + + $clone->select('*')->from('users')->whereBitNot('type', $type); + $this->assertEquals('select * from "users" where type & ? != ?', $clone->toSql()); + $clone->select('*')->from('users')->orWhereBitNot('flags', $flags); + $this->assertEquals('select * from "users" where type & ? != ? or flags & ? != ?', $clone->toSql()); + } + + public function testBitWheresOr() + { + $type = 16; + $flags = 32; + $builder = $this->getBuilder(); + $clone = $builder->clone(); + + $builder->select('*')->from('users')->whereBitOr('type', $type); + $this->assertEquals('select * from "users" where type | ? = ?', $builder->toSql()); + $builder->select('*')->from('users')->orWhereBitOr('flags', $flags); + $this->assertEquals('select * from "users" where type | ? = ? or flags | ? = ?', $builder->toSql()); + + $clone->select('*')->from('users')->whereBitOrNot('type', $type); + $this->assertEquals('select * from "users" where type | ? != ?', $clone->toSql()); + $clone->select('*')->from('users')->orWhereBitOrNot('flags', $flags); + $this->assertEquals('select * from "users" where type | ? != ? or flags | ? != ?', $clone->toSql()); + } + + public function testBitWheresXor() + { + $type = 16; + $flags = 32; + $builder = $this->getBuilder(); + $clone = $builder->clone(); + + $builder->select('*')->from('users')->whereBitXor('type', $type); + $this->assertEquals('select * from "users" where type ^ ? = ?', $builder->toSql()); + $builder->select('*')->from('users')->orWhereBitXor('flags', $flags); + $this->assertEquals('select * from "users" where type ^ ? = ? or flags ^ ? = ?', $builder->toSql()); + + $clone->select('*')->from('users')->whereBitXorNot('type', $type); + $this->assertEquals('select * from "users" where type ^ ? != ?', $clone->toSql()); + $clone->select('*')->from('users')->orWhereBitXorNot('flags', $flags); + $this->assertEquals('select * from "users" where type ^ ? != ? or flags ^ ? != ?', $clone->toSql()); + } + public function testClone() { $builder = $this->getBuilder();