Skip to content

Commit

Permalink
sql: allow get_bit() and set_bit() builtin functions to support byte …
Browse files Browse the repository at this point in the history
…array.

Fixes #45851

This commit modified get_bit and set_bit to allow them to support
byte array along with their respective testcases.
And modified doc and info of get_bit() and set_bit() for bit Array.

Release justification: low-risk change to existing functionality.

Release note (sql change): This PR is modified get_bits()
and set_bit() functions to allow them support byte array.
  • Loading branch information
abhishek20123g committed Mar 20, 2020
1 parent 744bf85 commit 7f44702
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 4 deletions.
8 changes: 6 additions & 2 deletions docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,9 @@ has no relationship with the commit order of concurrent transactions.</p>
</span></td></tr>
<tr><td><a name="from_uuid"></a><code>from_uuid(val: <a href="bytes.html">bytes</a>) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Converts the byte string representation of a UUID to its character string representation.</p>
</span></td></tr>
<tr><td><a name="get_bit"></a><code>get_bit(bit_string: varbit, index: <a href="int.html">int</a>) &rarr; <a href="int.html">int</a></code></td><td><span class="funcdesc"><p>Extracts a bit at given index in the bit array</p>
<tr><td><a name="get_bit"></a><code>get_bit(bit_string: varbit, index: <a href="int.html">int</a>) &rarr; <a href="int.html">int</a></code></td><td><span class="funcdesc"><p>Extracts a bit at given index in the bit array.</p>
</span></td></tr>
<tr><td><a name="get_bit"></a><code>get_bit(byte_string: <a href="bytes.html">bytes</a>, index: <a href="int.html">int</a>) &rarr; <a href="int.html">int</a></code></td><td><span class="funcdesc"><p>Extracts a bit at given index in the byte array.</p>
</span></td></tr>
<tr><td><a name="initcap"></a><code>initcap(val: <a href="string.html">string</a>) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Capitalizes the first letter of <code>val</code>.</p>
</span></td></tr>
Expand Down Expand Up @@ -1015,7 +1017,9 @@ has no relationship with the commit order of concurrent transactions.</p>
</span></td></tr>
<tr><td><a name="rtrim"></a><code>rtrim(val: <a href="string.html">string</a>) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Removes all spaces from the end (right-hand side) of <code>val</code>.</p>
</span></td></tr>
<tr><td><a name="set_bit"></a><code>set_bit(bit_string: varbit, index: <a href="int.html">int</a>, to_set: <a href="int.html">int</a>) &rarr; varbit</code></td><td><span class="funcdesc"><p>Updates a bit at given index in the bit array</p>
<tr><td><a name="set_bit"></a><code>set_bit(bit_string: varbit, index: <a href="int.html">int</a>, to_set: <a href="int.html">int</a>) &rarr; varbit</code></td><td><span class="funcdesc"><p>Updates a bit at given index in the bit array.</p>
</span></td></tr>
<tr><td><a name="set_bit"></a><code>set_bit(byte_string: <a href="bytes.html">bytes</a>, index: <a href="int.html">int</a>, to_set: <a href="int.html">int</a>) &rarr; <a href="bytes.html">bytes</a></code></td><td><span class="funcdesc"><p>Updates a bit at given index in the byte array.</p>
</span></td></tr>
<tr><td><a name="sha1"></a><code>sha1(<a href="bytes.html">bytes</a>...) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Calculates the SHA1 hash value of a set of values.</p>
</span></td></tr>
Expand Down
53 changes: 53 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/builtin_function
Original file line number Diff line number Diff line change
Expand Up @@ -2584,6 +2584,34 @@ SELECT get_bit(B'10110', 10)
query error get_bit\(\): GetBitAtIndex: bit index 0 out of valid range \(0..-1\)
SELECT get_bit(B'', 0);

# Binary representation of 'l' is 01101100
# Binary representation of \o145 is 01100101
# Binary representation of \x61\x62\x6C are 01100001 01100010 01101100

query I rowsort
SELECT get_bit(b'\145\x6C\l', 0) UNION SELECT get_bit(b'\145\x6C\l', 13)
----
0
1

query I rowsort
SELECT get_bit(b'\145', 7) UNION SELECT get_bit(b'\145', 0)
----
1
0

query I rowsort
SELECT get_bit('\x6162'::bytea, 7) UNION SELECT get_bit('\x6162'::bytea, 12)
----
1
0

query error get_bit\(\): bit index 8 out of valid range \(0..7\)
SELECT get_bit(b'\x61', 8)

query error get_bit\(\): bit index 0 out of valid range \(0..-1\)
SELECT get_bit(b'', 0)

subtest set_bit

query T rowsort
Expand All @@ -2606,3 +2634,28 @@ SELECT set_bit(B'1001010', 0, 2)

query error set_bit\(\): SetBitAtIndex: bit index 0 out of valid range \(0..-1\)
SELECT set_bit(B'', 0, 1)

# Binary representation of 'a' 'b' 'c' 'f' 'l' are 01100001 01100010 01100011 01100110 01101100
# Binary representation of \o145 is 1100101
# Binary representation of \x61\x62\x66\x6C are 01100001 01100010 01100110 01101100

query T rowsort
SELECT set_bit(b'ab', 6, 1) UNION SELECT set_bit(b'\x61\x66', 15, 0)
----
cb
af

query T rowsort
SELECT set_bit('a'::bytea, 5, 0) UNION SELECT set_bit('\x6162'::bytea, 13, 1)
----
a
af

query error set_bit\(\): bit index 16 out of valid range \(0..15\)
SELECT set_bit(b'ac', 16, 0)

query error set_bit\(\): bit index 0 out of valid range \(0..-1\)
SELECT set_bit(b'', 0, 1)

query error set_bit\(\): new bit must be 0 or 1.
SELECT set_bit(b'\145\x6C\l', 0, 2)
57 changes: 55 additions & 2 deletions pkg/sql/sem/builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,28 @@ var builtins = map[string]builtinDefinition{
}
return tree.NewDInt(tree.DInt(bit)), nil
},
Info: "Extracts a bit at given index in the bit array",
Info: "Extracts a bit at given index in the bit array.",
},
tree.Overload{
Types: tree.ArgTypes{{"byte_string", types.Bytes}, {"index", types.Int}},
ReturnType: tree.FixedReturnType(types.Int),
Fn: func(_ *tree.EvalContext, args tree.Datums) (tree.Datum, error) {
byteString := []byte(*args[0].(*tree.DBytes))
index := int(tree.MustBeDInt(args[1]))
// Check whether index asked is inside ByteArray.
if index < 0 || index >= 8*len(byteString) {
return nil, pgerror.Newf(pgcode.ArraySubscript,
"bit index %d out of valid range (0..%d)", index, 8*len(byteString)-1)
}
// To extract a bit at the given index, we have to determine the
// position within byte array, i.e. index/8 after that checked
// the bit at residual index.
if byteString[index/8]&(byte(1)<<(8-1-byte(index)%8)) != 0 {
return tree.NewDInt(tree.DInt(1)), nil
}
return tree.NewDInt(tree.DInt(0)), nil
},
Info: "Extracts a bit at given index in the byte array.",
}),

// https://www.postgresql.org/docs/9.0/functions-binarystring.html#FUNCTIONS-BINARYSTRING-OTHER
Expand Down Expand Up @@ -363,7 +384,39 @@ var builtins = map[string]builtinDefinition{
}
return &tree.DBitArray{BitArray: updatedBitString}, nil
},
Info: "Updates a bit at given index in the bit array",
Info: "Updates a bit at given index in the bit array.",
},
tree.Overload{
Types: tree.ArgTypes{
{"byte_string", types.Bytes},
{"index", types.Int},
{"to_set", types.Int},
},
ReturnType: tree.FixedReturnType(types.Bytes),
Fn: func(_ *tree.EvalContext, args tree.Datums) (tree.Datum, error) {
byteString := []byte(*args[0].(*tree.DBytes))
index := int(tree.MustBeDInt(args[1]))
toSet := int(tree.MustBeDInt(args[2]))
// Value of bit can only be set to 1 or 0.
if toSet != 0 && toSet != 1 {
return nil, pgerror.Newf(pgcode.InvalidParameterValue,
"new bit must be 0 or 1.")
}
// Check whether index asked is inside ByteArray.
if index < 0 || index >= 8*len(byteString) {
return nil, pgerror.Newf(pgcode.ArraySubscript,
"bit index %d out of valid range (0..%d)", index, 8*len(byteString)-1)
}
// To update a bit at the given index, we have to determine the
// position within byte array, i.e. index/8 after that checked
// the bit at residual index.
// Forcefully making bit at the index to 0.
byteString[index/8] &= ^(byte(1) << (8 - 1 - byte(index)%8))
// Updating value at the index to toSet.
byteString[index/8] |= byte(toSet) << (8 - 1 - byte(index)%8)
return tree.NewDBytes(tree.DBytes(byteString)), nil
},
Info: "Updates a bit at given index in the byte array.",
}),

"gen_random_uuid": makeBuiltin(
Expand Down

0 comments on commit 7f44702

Please sign in to comment.