Files
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

769 lines
22 KiB
Go

// Copyright 2022 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 encoding
import (
"context"
"fmt"
fb "github.com/dolthub/flatbuffers/v23/go"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/expression/function/vector"
"github.com/dolthub/go-mysql-server/sql/planbuilder"
sqltypes "github.com/dolthub/go-mysql-server/sql/types"
"github.com/dolthub/dolt/go/gen/fb/serial"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
"github.com/dolthub/dolt/go/store/types"
"github.com/dolthub/dolt/go/store/val"
)
const (
builderBufferSize = 1500
keylessIdCol = "keyless_hash_id"
keylessCardCol = "keyless_cardinality"
)
// SerializeSchema serializes a schema.Schema as a Flatbuffer message wrapped in a serial.Message.
func SerializeSchema(ctx context.Context, vrw types.ValueReadWriter, sch schema.Schema) (types.SerialMessage, error) {
buf, err := serializeSchemaAsFlatbuffer(ctx, sch)
if err != nil {
return nil, err
}
v := types.SerialMessage(buf)
if _, err = vrw.WriteValue(ctx, v); err != nil {
return nil, err
}
return v, nil
}
func serializeSchemaAsFlatbuffer(ctx context.Context, sch schema.Schema) ([]byte, error) {
b := fb.NewBuilder(1024)
columns := serializeSchemaColumns(ctx, b, sch)
rows := serializeClusteredIndex(b, sch)
indexes := serializeSecondaryIndexes(b, sch, sch.Indexes().AllIndexes())
checks := serializeChecks(b, sch.Checks().AllChecks())
comment := b.CreateString(sch.GetComment())
var hasFeaturesAfterTryAccessors bool
for _, col := range sch.GetAllCols().GetColumns() {
if col.OnUpdate != "" {
hasFeaturesAfterTryAccessors = true
break
}
}
serial.TableSchemaStart(b)
serial.TableSchemaAddClusteredIndex(b, rows)
serial.TableSchemaAddColumns(b, columns)
serial.TableSchemaAddSecondaryIndexes(b, indexes)
serial.TableSchemaAddChecks(b, checks)
serial.TableSchemaAddCollation(b, serial.Collation(sch.GetCollation()))
if sch.GetComment() != "" {
serial.TableSchemaAddComment(b, comment)
hasFeaturesAfterTryAccessors = true
}
if targetRowSize := sch.GetTargetRowSize(); targetRowSize != val.DefaultTupleLengthTarget {
serial.TableSchemaAddTargetRowSize(b, targetRowSize)
hasFeaturesAfterTryAccessors = true
}
if hasFeaturesAfterTryAccessors {
serial.TableSchemaAddHasFeaturesAfterTryAccessors(b, hasFeaturesAfterTryAccessors)
}
root := serial.TableSchemaEnd(b)
bs := serial.FinishMessage(b, root, []byte(serial.TableSchemaFileID))
return bs, nil
}
// DeserializeSchema deserializes a schema.Schema from a serial.Message.
func DeserializeSchema(ctx context.Context, nbf *types.NomsBinFormat, v types.Value) (schema.Schema, error) {
sm, ok := v.(types.SerialMessage)
assertTrue(ok, "must pass types.SerialMessage value to DeserializeSchema")
return deserializeSchemaFromFlatbuffer(ctx, sm)
}
func deserializeSchemaFromFlatbuffer(ctx context.Context, buf []byte) (schema.Schema, error) {
assertTrue(serial.GetFileID(buf) == serial.TableSchemaFileID, "serialized schema must have FileID == TableSchemaFileID")
s, err := serial.TryGetRootAsTableSchema(buf, serial.MessagePrefixSz)
if err != nil {
return nil, err
}
cols, err := deserializeColumns(ctx, s)
if err != nil {
return nil, err
}
sch, err := schema.SchemaFromCols(schema.NewColCollection(cols...))
if err != nil {
return nil, err
}
dci, err := deserializeClusteredIndex(s)
if err != nil {
return nil, err
}
err = sch.SetPkOrdinals(dci)
if err != nil {
return nil, err
}
err = deserializeSecondaryIndexes(sch, s)
if err != nil {
return nil, err
}
err = deserializeChecks(sch, s)
if err != nil {
return nil, err
}
sch.SetCollation(schema.Collation(s.Collation()))
sch.SetComment(string(s.Comment()))
sch.SetTargetRowSize(s.TargetRowSize())
return sch, nil
}
// clustered indexes
func serializeClusteredIndex(b *fb.Builder, sch schema.Schema) fb.UOffsetT {
keyless := schema.IsKeyless(sch)
// serialize key columns
var ko fb.UOffsetT
if keyless {
// keyless id is the 2nd to last column
// in the columns vector (by convention)
// and the only field in key tuples of
// the clustered index.
idPos := sch.GetAllCols().Size()
serial.IndexStartIndexColumnsVector(b, 1)
b.PrependUint16(uint16(idPos))
ko = b.EndVector(1)
} else {
pkMap := sch.GetPkOrdinals()
serial.IndexStartIndexColumnsVector(b, len(pkMap))
for i := len(pkMap) - 1; i >= 0; i-- {
b.PrependUint16(uint16(pkMap[i]))
}
ko = b.EndVector(len(pkMap))
}
// serialize value columns
nonPk := sch.GetNonPKCols().GetColumns()
length := len(nonPk)
if keyless {
length++
}
serial.IndexStartValueColumnsVector(b, length)
for i := len(nonPk) - 1; i >= 0; i-- {
col := nonPk[i]
pos := sch.GetAllCols().TagToIdx[col.Tag]
b.PrependUint16(uint16(pos))
}
if keyless {
// keyless cardinality is the last column
// in the columns vector (by convention)
// and the first field in value tuples of
// the clustered index.
cardPos := sch.GetAllCols().Size() + 1
b.PrependUint16(uint16(cardPos))
}
vo := b.EndVector(length)
serial.IndexStart(b)
// key_columns == index_columns for clustered index
serial.IndexAddIndexColumns(b, ko)
serial.IndexAddKeyColumns(b, ko)
serial.IndexAddValueColumns(b, vo)
serial.IndexAddPrimaryKey(b, true)
serial.IndexAddUniqueKey(b, true)
serial.IndexAddSpatialKey(b, false)
serial.IndexAddSystemDefined(b, false)
return serial.IndexEnd(b)
}
func deserializeClusteredIndex(s *serial.TableSchema) ([]int, error) {
// check for keyless schema
kss, err := keylessSerialSchema(s)
if err != nil {
return nil, err
}
if kss {
return nil, nil
}
ci, err := s.TryClusteredIndex(nil)
if err != nil {
return nil, err
}
pkOrdinals := make([]int, ci.KeyColumnsLength())
for i := range pkOrdinals {
pkOrdinals[i] = int(ci.KeyColumns(i))
}
return pkOrdinals, nil
}
func serializeSchemaColumns(ctx context.Context, b *fb.Builder, sch schema.Schema) fb.UOffsetT {
cols := sch.GetAllCols().GetColumns()
offs := make([]fb.UOffsetT, len(cols))
if schema.IsKeyless(sch) {
// (6/15/22)
// currently, keyless id and cardinality columns
// do not exist in schema.Schema
// we do serialize them in the flatbuffer
// message, in order to describe index storage.
// by convention, they are stored as the last
// two columns in the columns vector.
id, card := serializeHiddenKeylessColumns(b)
offs = append(offs, id, card)
}
// serialize columns in |cols|
for i := len(cols) - 1; i >= 0; i-- {
col := cols[i]
var defVal, onUpdateVal string
if col.Default != "" {
defVal = col.Default
} else {
defVal = col.Generated
}
if col.OnUpdate != "" {
onUpdateVal = col.OnUpdate
}
co := b.CreateString(col.Comment)
do := b.CreateString(defVal)
ou := b.CreateString(onUpdateVal)
typeString := sqlTypeString(ctx, col.TypeInfo)
to := b.CreateString(typeString)
no := b.CreateString(col.Name)
serial.ColumnStart(b)
serial.ColumnAddName(b, no)
serial.ColumnAddSqlType(b, to)
serial.ColumnAddDefaultValue(b, do)
serial.ColumnAddComment(b, co)
// schema.Schema determines display order
serial.ColumnAddDisplayOrder(b, int16(i))
serial.ColumnAddTag(b, col.Tag)
serial.ColumnAddEncoding(b, serial.Encoding(col.TypeInfo.Encoding()))
serial.ColumnAddPrimaryKey(b, col.IsPartOfPK)
serial.ColumnAddAutoIncrement(b, col.AutoIncrement)
serial.ColumnAddNullable(b, col.IsNullable())
serial.ColumnAddGenerated(b, col.Generated != "")
serial.ColumnAddVirtual(b, col.Virtual)
if onUpdateVal != "" {
serial.ColumnAddOnUpdateValue(b, ou)
}
// Only write the adaptive encoding field if the column uses adaptive encoding. This will force older clients that
// don't know about this field to update in order to read it. Older versions of Dolt ignored the serialized
// |encoding| field and inferred the encoding based on column type, which means they would try to interpret an
// adaptive encoded field as a literal value.
if usesAdaptiveEncoding(col) {
serial.ColumnAddUsesAdaptiveEncoding(b, true)
serial.ColumnAddAdaptiveEncodingBreakingChange(b, true)
}
if col.Hidden {
serial.ColumnAddHidden(b, col.Hidden)
}
if col.SystemHidden {
serial.ColumnAddHiddenSystem(b, col.SystemHidden)
}
offs[i] = serial.ColumnEnd(b)
}
// create the columns array with all columns
serial.TableSchemaStartColumnsVector(b, len(offs))
for i := len(offs) - 1; i >= 0; i-- {
b.PrependUOffsetT(offs[i])
}
return b.EndVector(len(offs))
}
func usesAdaptiveEncoding(col schema.Column) bool {
switch col.TypeInfo.Encoding() {
// val.ExtendedAdaptiveEnc is absent from this list because the extended types have their own ser / deser logic
case val.BytesAdaptiveEnc, val.StringAdaptiveEnc, val.GeomAdaptiveEnc, val.JsonAdaptiveEnc:
return true
default:
return false
}
}
func serializeHiddenKeylessColumns(b *fb.Builder) (id, card fb.UOffsetT) {
// cardinality column
no := b.CreateString(keylessCardCol)
serial.ColumnStart(b)
serial.ColumnAddName(b, no)
serial.ColumnAddDisplayOrder(b, int16(-1))
serial.ColumnAddTag(b, schema.KeylessRowCardinalityTag)
serial.ColumnAddEncoding(b, serial.EncodingUint64)
// set hidden and generated to true
serial.ColumnAddGenerated(b, true)
serial.ColumnAddHidden(b, true)
serial.ColumnAddPrimaryKey(b, false)
serial.ColumnAddAutoIncrement(b, false)
serial.ColumnAddNullable(b, false)
serial.ColumnAddVirtual(b, false)
card = serial.ColumnEnd(b)
// hash id column
no = b.CreateString(keylessIdCol)
serial.ColumnStart(b)
serial.ColumnAddName(b, no)
serial.ColumnAddDisplayOrder(b, int16(-1))
serial.ColumnAddTag(b, schema.KeylessRowIdTag)
serial.ColumnAddEncoding(b, serial.EncodingHash128)
// set hidden and generated to true
serial.ColumnAddGenerated(b, true)
serial.ColumnAddHidden(b, true)
serial.ColumnAddPrimaryKey(b, false)
serial.ColumnAddAutoIncrement(b, false)
serial.ColumnAddNullable(b, false)
serial.ColumnAddVirtual(b, false)
id = serial.ColumnEnd(b)
return
}
func deserializeColumns(ctx context.Context, s *serial.TableSchema) ([]schema.Column, error) {
length := s.ColumnsLength()
isKeyless, err := keylessSerialSchema(s)
if err != nil {
return nil, err
}
if isKeyless {
// (6/15/22)
// currently, keyless id and cardinality columns
// do not exist in schema.Schema
// we do serialize them in the flatbuffer
// message, in order to describe index storage.
// by convention, they are stored as the last
// two columns in the columns vector.
length -= 2
}
cols := make([]schema.Column, length)
c := serial.Column{}
for i := range cols {
_, err := s.TryColumns(&c, i)
if err != nil {
return nil, err
}
sqlType, err := typeinfoFromSqlType(ctx, string(c.SqlType()))
if err != nil {
return nil, err
}
sqlType = sqlType.WithEncoding(val.Encoding(c.Encoding()))
var defVal, generatedVal, onUpdateVal string
if c.DefaultValue() != nil {
if c.Generated() {
generatedVal = string(c.DefaultValue())
} else {
defVal = string(c.DefaultValue())
}
}
if c.OnUpdateValue() != nil {
onUpdateVal = string(c.OnUpdateValue())
}
cols[i] = schema.Column{
Name: string(c.Name()),
Tag: c.Tag(),
Kind: sqlType.NomsKind(),
IsPartOfPK: c.PrimaryKey(),
TypeInfo: sqlType,
Default: defVal,
Generated: generatedVal,
OnUpdate: onUpdateVal,
Virtual: c.Virtual(),
AutoIncrement: c.AutoIncrement(),
Comment: string(c.Comment()),
Constraints: constraintsFromSerialColumn(&c),
Hidden: c.Hidden(),
SystemHidden: c.HiddenSystem(),
}
}
return cols, nil
}
func serializeSecondaryIndexes(b *fb.Builder, sch schema.Schema, indexes []schema.Index) fb.UOffsetT {
ordinalMap := sch.GetAllCols().TagToIdx
offs := make([]fb.UOffsetT, len(indexes))
for i := len(offs) - 1; i >= 0; i-- {
idx := indexes[i]
no := b.CreateString(idx.Name())
co := b.CreateString(idx.Comment())
var predOffset fb.UOffsetT
if idx.Predicate() != "" {
predOffset = b.CreateString(idx.Predicate())
}
// serialize indexed columns
tags := idx.IndexedColumnTags()
serial.IndexStartIndexColumnsVector(b, len(tags))
for j := len(tags) - 1; j >= 0; j-- {
pos := ordinalMap[tags[j]]
b.PrependUint16(uint16(pos))
}
ico := b.EndVector(len(tags))
// serialize key columns
tags = idx.AllTags()
serial.IndexStartKeyColumnsVector(b, len(tags))
for j := len(tags) - 1; j >= 0; j-- {
pos := ordinalMap[tags[j]]
b.PrependUint16(uint16(pos))
}
ko := b.EndVector(len(tags))
// serialize prefix lengths
prefixLengths := idx.PrefixLengths()
serial.IndexStartPrefixLengthsVector(b, len(prefixLengths))
for j := len(prefixLengths) - 1; j >= 0; j-- {
b.PrependUint16(prefixLengths[j])
}
po := b.EndVector(len(prefixLengths))
var ftInfo fb.UOffsetT
if idx.IsFullText() {
ftInfo = serializeFullTextInfo(b, idx)
}
var vectorInfo fb.UOffsetT
if idx.IsVector() {
vectorInfo = serializeVectorInfo(b, idx)
}
serial.IndexStart(b)
serial.IndexAddName(b, no)
serial.IndexAddComment(b, co)
serial.IndexAddIndexColumns(b, ico)
serial.IndexAddKeyColumns(b, ko)
serial.IndexAddPrimaryKey(b, false)
serial.IndexAddUniqueKey(b, idx.IsUnique())
serial.IndexAddSystemDefined(b, !idx.IsUserDefined())
serial.IndexAddPrefixLengths(b, po)
serial.IndexAddSpatialKey(b, idx.IsSpatial())
serial.IndexAddFulltextKey(b, idx.IsFullText())
if idx.IsFullText() {
serial.IndexAddFulltextInfo(b, ftInfo)
}
if idx.IsVector() {
serial.IndexAddVectorKey(b, true)
serial.IndexAddVectorInfo(b, vectorInfo)
}
if idx.Predicate() != "" {
serial.IndexAddPredicate(b, predOffset)
}
offs[i] = serial.IndexEnd(b)
}
serial.TableSchemaStartSecondaryIndexesVector(b, len(indexes))
for i := len(offs) - 1; i >= 0; i-- {
b.PrependUOffsetT(offs[i])
}
return b.EndVector(len(indexes))
}
func deserializeSecondaryIndexes(sch schema.Schema, s *serial.TableSchema) error {
idx := serial.Index{}
col := serial.Column{}
for i := 0; i < s.SecondaryIndexesLength(); i++ {
_, err := s.TrySecondaryIndexes(&idx, i)
if err != nil {
return err
}
assertTrue(!idx.PrimaryKey(), "cannot deserialize secondary index with PrimaryKey() == true")
fti, err := deserializeFullTextInfo(&idx)
if err != nil {
return err
}
vi, err := deserializeVectorInfo(&idx)
if err != nil {
return err
}
name := string(idx.Name())
props := schema.IndexProperties{
IsUnique: idx.UniqueKey(),
IsSpatial: idx.SpatialKey(),
IsFullText: idx.FulltextKey(),
IsVector: idx.VectorKey(),
IsUserDefined: !idx.SystemDefined(),
Comment: string(idx.Comment()),
Predicate: string(idx.Predicate()),
FullTextProperties: fti,
VectorProperties: vi,
}
tags := make([]uint64, idx.IndexColumnsLength())
for j := range tags {
pos := idx.IndexColumns(j)
_, err := s.TryColumns(&col, int(pos))
if err != nil {
return err
}
tags[j] = col.Tag()
}
var prefixLengths []uint16
prefixLengthsLength := idx.PrefixLengthsLength()
if prefixLengthsLength > 0 {
prefixLengths = make([]uint16, prefixLengthsLength)
for j := range prefixLengths {
prefixLengths[j] = idx.PrefixLengths(j)
}
}
_, err = sch.Indexes().AddIndexByColTags(name, tags, prefixLengths, props)
if err != nil {
return err
}
}
return nil
}
func serializeChecks(b *fb.Builder, checks []schema.Check) fb.UOffsetT {
offs := make([]fb.UOffsetT, len(checks))
for i := len(offs) - 1; i >= 0; i-- {
eo := b.CreateString(checks[i].Expression())
no := b.CreateString(checks[i].Name())
serial.CheckConstraintStart(b)
serial.CheckConstraintAddEnforced(b, checks[i].Enforced())
serial.CheckConstraintAddExpression(b, eo)
serial.CheckConstraintAddName(b, no)
serial.CheckConstraintAddIsNotValid(b, checks[i].IsNotValid())
offs[i] = serial.CheckConstraintEnd(b)
}
serial.TableSchemaStartChecksVector(b, len(checks))
for i := len(offs) - 1; i >= 0; i-- {
b.PrependUOffsetT(offs[i])
}
return b.EndVector(len(checks))
}
func deserializeChecks(sch schema.Schema, s *serial.TableSchema) error {
coll := sch.Checks()
c := serial.CheckConstraint{}
for i := 0; i < s.ChecksLength(); i++ {
_, err := s.TryChecks(&c, i)
if err != nil {
return err
}
n, e := string(c.Name()), string(c.Expression())
if _, err := coll.AddCheck(n, e, c.Enforced(), c.IsNotValid()); err != nil {
return err
}
}
return nil
}
func serializeFullTextInfo(b *fb.Builder, idx schema.Index) fb.UOffsetT {
props := idx.FullTextProperties()
configTable := b.CreateString(props.ConfigTable)
posTable := b.CreateString(props.PositionTable)
docCountTable := b.CreateString(props.DocCountTable)
globalCountTable := b.CreateString(props.GlobalCountTable)
rowCountTable := b.CreateString(props.RowCountTable)
keyName := b.CreateString(props.KeyName)
keyPositions := idx.FullTextProperties().KeyPositions
serial.FulltextInfoStartKeyPositionsVector(b, len(keyPositions))
for j := len(keyPositions) - 1; j >= 0; j-- {
b.PrependUint16(keyPositions[j])
}
keyPos := b.EndVector(len(keyPositions))
serial.FulltextInfoStart(b)
serial.FulltextInfoAddConfigTable(b, configTable)
serial.FulltextInfoAddPositionTable(b, posTable)
serial.FulltextInfoAddDocCountTable(b, docCountTable)
serial.FulltextInfoAddGlobalCountTable(b, globalCountTable)
serial.FulltextInfoAddRowCountTable(b, rowCountTable)
serial.FulltextInfoAddKeyType(b, props.KeyType)
serial.FulltextInfoAddKeyName(b, keyName)
serial.FulltextInfoAddKeyPositions(b, keyPos)
return serial.FulltextInfoEnd(b)
}
func serializeVectorInfo(b *fb.Builder, idx schema.Index) fb.UOffsetT {
props := idx.VectorProperties()
serial.VectorInfoStart(b)
switch props.DistanceType {
case vector.DistanceL2Squared{}:
serial.VectorInfoAddDistanceType(b, serial.DistanceTypeL2_Squared)
}
return serial.VectorInfoEnd(b)
}
func deserializeFullTextInfo(idx *serial.Index) (schema.FullTextProperties, error) {
fulltext := serial.FulltextInfo{}
has, err := idx.TryFulltextInfo(&fulltext)
if err != nil {
return schema.FullTextProperties{}, err
}
if has == nil {
return schema.FullTextProperties{}, nil
}
var keyPositions []uint16
keyPositionsLength := fulltext.KeyPositionsLength()
if keyPositionsLength > 0 {
keyPositions = make([]uint16, keyPositionsLength)
for j := range keyPositions {
keyPositions[j] = fulltext.KeyPositions(j)
}
}
return schema.FullTextProperties{
ConfigTable: string(fulltext.ConfigTable()),
PositionTable: string(fulltext.PositionTable()),
DocCountTable: string(fulltext.DocCountTable()),
GlobalCountTable: string(fulltext.GlobalCountTable()),
RowCountTable: string(fulltext.RowCountTable()),
KeyType: fulltext.KeyType(),
KeyName: string(fulltext.KeyName()),
KeyPositions: keyPositions,
}, nil
}
func deserializeVectorInfo(idx *serial.Index) (schema.VectorProperties, error) {
vectorInfo := serial.VectorInfo{}
has, err := idx.TryVectorInfo(&vectorInfo)
if err != nil {
return schema.VectorProperties{}, err
}
if has == nil {
return schema.VectorProperties{}, nil
}
switch vectorInfo.DistanceType() {
case serial.DistanceTypeL2_Squared:
return schema.VectorProperties{
DistanceType: vector.DistanceL2Squared{},
}, nil
}
return schema.VectorProperties{}, fmt.Errorf("unknown distance type in vector index info: %s", vectorInfo.DistanceType())
}
func keylessSerialSchema(s *serial.TableSchema) (bool, error) {
n := s.ColumnsLength()
if n < 2 {
return false, nil
}
// keyless id is the 2nd to last column
// in the columns vector (by convention)
// and the only field in key tuples of
// the clustered index.
id := serial.Column{}
_, err := s.TryColumns(&id, n-2)
if err != nil {
return false, err
}
ok := id.Generated() && id.Hidden() &&
string(id.Name()) == keylessIdCol
if !ok {
return false, nil
}
// keyless cardinality is the last column
// in the columns vector (by convention)
// and the first field in value tuples of
// the clustered index.
card := serial.Column{}
_, err = s.TryColumns(&card, n-1)
if err != nil {
return false, err
}
return card.Generated() && card.Hidden() &&
string(card.Name()) == keylessCardCol, nil
}
func sqlTypeString(ctx context.Context, t typeinfo.TypeInfo) string {
typ := t.ToSqlType()
if st, ok := typ.(sql.SpatialColumnType); ok {
// for spatial types, we must append the SRID
if srid, ok := st.GetSpatialTypeSRID(); ok {
return fmt.Sprintf("%s SRID %d", typ.String(), srid)
}
}
// For datetime types, always store the precision explicitly so that it can be read back precisely, although MySQL
// omits the precision when it's 0 (the default).
if sqltypes.IsDatetimeType(typ) || sqltypes.IsTimestampType(typ) {
dt := typ.(sql.DatetimeType)
if dt.Precision() == 0 {
return fmt.Sprintf("%s(0)", typ.String())
}
return typ.String()
}
// Extended types are string serializable, so we'll just prepend a tag
if extendedType, ok := typ.(sql.ExtendedType); ok {
sqlCtx, ok := ctx.(*sql.Context)
if !ok {
panic("SQL context needed for extended type deserialization")
}
serializedType, err := sqltypes.SerializeTypeToString(sqlCtx, extendedType)
if err != nil {
panic(err)
}
return planbuilder.ExtendedTypeTag + serializedType
}
return typ.String()
}
func typeinfoFromSqlType(ctx context.Context, s string) (typeinfo.TypeInfo, error) {
sqlType, err := planbuilder.ParseColumnTypeString(ctx, s)
if err != nil {
return nil, err
}
return typeinfo.FromSqlType(sqlType)
}
func constraintsFromSerialColumn(col *serial.Column) (cc []schema.ColConstraint) {
if !col.Nullable() || col.PrimaryKey() {
cc = append(cc, schema.NotNullConstraint{})
}
return
}
func assertTrue(b bool, msg string) {
if !b {
panic("assertion failed: " + msg)
}
}