Files
dolthub--doltgresql/server/functions/bit.go
T
2026-07-13 12:32:25 +08:00

173 lines
5.2 KiB
Go

// Copyright 2024 Dolthub, Inc.
//
// 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.
package functions
import (
"fmt"
"strconv"
"strings"
"github.com/cockroachdb/errors"
"github.com/dolthub/go-mysql-server/sql"
"github.com/jackc/pgtype"
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
"github.com/dolthub/doltgresql/server/functions/framework"
pgtypes "github.com/dolthub/doltgresql/server/types"
"github.com/dolthub/doltgresql/utils"
)
// initBit registers the functions to the catalog.
func initBit() {
framework.RegisterFunction(bitin)
framework.RegisterFunction(bitout)
framework.RegisterFunction(bitrecv)
framework.RegisterFunction(bitsend)
framework.RegisterFunction(bittypmodin)
framework.RegisterFunction(bittypmodout)
}
// bitin represents the PostgreSQL function of bit type IO input.
var bitin = framework.Function3{
Name: "bit_in",
Return: pgtypes.Bit,
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Cstring, pgtypes.Oid, pgtypes.Int32},
Strict: true,
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, _, val3 any) (any, error) {
input := val1.(string)
typmod := val3.(int32)
// validation and normalization
array, err := tree.ParseDBitArray(input)
if err != nil {
return nil, err
}
if array.BitLen() != uint(typmod) {
return nil, pgtypes.ErrWrongLengthBit.New(len(input), typmod)
}
return tree.AsStringWithFlags(array, tree.FmtPgwireText), nil
},
}
// bitout represents the PostgreSQL function of bit type IO output.
var bitout = framework.Function1{
Name: "bit_out",
Return: pgtypes.Cstring,
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Bit},
Strict: true,
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
bitStr := val.(string)
return bitStr, nil
},
}
// bitrecv represents the PostgreSQL function of bit type IO receive.
var bitrecv = framework.Function3{
Name: "bit_recv",
Return: pgtypes.Bit,
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Internal, pgtypes.Oid, pgtypes.Int32},
Strict: true,
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
data := val1.([]byte)
if data == nil {
return nil, nil
}
typmod := val3.(int32)
var out pgtype.Bit
err := out.DecodeBinary(nil, data)
if err != nil {
return nil, err
}
var str string
for _, b := range out.Bytes {
str += fmt.Sprintf("%08b", b)
}
str = str[:out.Len]
if typmod != -1 {
str = str[:typmod]
}
return str, nil
},
}
// bitsend represents the PostgreSQL function of bit type IO send.
var bitsend = framework.Function1{
Name: "bit_send",
Return: pgtypes.Bytea,
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Bit},
Strict: true,
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
if wrapper, ok := val.(sql.AnyWrapper); ok {
var err error
val, err = wrapper.UnwrapAny(ctx)
if err != nil {
return nil, err
}
if val == nil {
return nil, nil
}
}
// We process bits in chunks of 8, so we append zeroes until our string is evenly divisible by 8
bitString := val.(string)
if len(bitString)%8 != 0 {
bitString += strings.Repeat("0", 8-(len(bitString)%8))
}
writer := utils.NewWireWriter()
writer.Reserve(uint64(4 + (len(bitString) / 8)))
writer.WriteInt32(t[0].GetAttTypMod())
for bufIdx := 0; bufIdx < len(bitString); bufIdx += 8 {
parsedByte, err := strconv.ParseUint(bitString[bufIdx:bufIdx+8], 2, 8)
if err != nil {
return nil, errors.Errorf(`error encountered while converting "BIT" to binary wire format:\n%s`, err.Error())
}
writer.WriteUint8(byte(parsedByte))
}
return writer.BufferData(), nil
},
}
// bittypmodin represents the PostgreSQL function of bit type IO typmod input.
var bittypmodin = framework.Function1{
Name: "bittypmodin",
Return: pgtypes.Int32,
Parameters: [1]*pgtypes.DoltgresType{pgtypes.CstringArray},
Strict: true,
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
typmod, err := getTypModFromStringArr("bit", val.([]any))
if err != nil {
return nil, err
}
// getTypModFromStringArr always adds 4, so we remove 4 here since it doesn't apply to bit types
return typmod - 4, nil
},
}
// bittypmodout represents the PostgreSQL function of bit type IO typmod output.
var bittypmodout = framework.Function1{
Name: "bittypmodout",
Return: pgtypes.Cstring,
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int32},
Strict: true,
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
typmod := val.(int32)
if typmod < 1 {
return "", nil
}
return fmt.Sprintf("(%v)", typmod), nil
},
}