github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/schema/encoding/serialization.go (about) 1 // Copyright 2022 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package encoding 16 17 import ( 18 "context" 19 "fmt" 20 21 fb "github.com/dolthub/flatbuffers/v23/go" 22 "github.com/dolthub/go-mysql-server/sql" 23 "github.com/dolthub/go-mysql-server/sql/planbuilder" 24 sqltypes "github.com/dolthub/go-mysql-server/sql/types" 25 26 "github.com/dolthub/dolt/go/gen/fb/serial" 27 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 28 "github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo" 29 "github.com/dolthub/dolt/go/store/types" 30 ) 31 32 const ( 33 builderBufferSize = 1500 34 35 keylessIdCol = "keyless_hash_id" 36 keylessCardCol = "keyless_cardinality" 37 ) 38 39 // SerializeSchema serializes a schema.Schema as a Flatbuffer message wrapped in a serial.Message. 40 func SerializeSchema(ctx context.Context, vrw types.ValueReadWriter, sch schema.Schema) (types.SerialMessage, error) { 41 buf, err := serializeSchemaAsFlatbuffer(sch) 42 if err != nil { 43 return nil, err 44 } 45 46 v := types.SerialMessage(buf) 47 if _, err = vrw.WriteValue(ctx, v); err != nil { 48 return nil, err 49 } 50 return v, nil 51 } 52 53 func serializeSchemaAsFlatbuffer(sch schema.Schema) ([]byte, error) { 54 b := fb.NewBuilder(1024) 55 columns := serializeSchemaColumns(b, sch) 56 rows := serializeClusteredIndex(b, sch) 57 indexes := serializeSecondaryIndexes(b, sch, sch.Indexes().AllIndexes()) 58 checks := serializeChecks(b, sch.Checks().AllChecks()) 59 comment := b.CreateString(sch.GetComment()) 60 61 var hasFeaturesAfterTryAccessors bool 62 for _, col := range sch.GetAllCols().GetColumns() { 63 if col.OnUpdate != "" { 64 hasFeaturesAfterTryAccessors = true 65 break 66 } 67 } 68 69 serial.TableSchemaStart(b) 70 serial.TableSchemaAddClusteredIndex(b, rows) 71 serial.TableSchemaAddColumns(b, columns) 72 serial.TableSchemaAddSecondaryIndexes(b, indexes) 73 serial.TableSchemaAddChecks(b, checks) 74 serial.TableSchemaAddCollation(b, serial.Collation(sch.GetCollation())) 75 if sch.GetComment() != "" { 76 serial.TableSchemaAddComment(b, comment) 77 hasFeaturesAfterTryAccessors = true 78 } 79 if hasFeaturesAfterTryAccessors { 80 serial.TableSchemaAddHasFeaturesAfterTryAccessors(b, hasFeaturesAfterTryAccessors) 81 } 82 root := serial.TableSchemaEnd(b) 83 bs := serial.FinishMessage(b, root, []byte(serial.TableSchemaFileID)) 84 return bs, nil 85 } 86 87 // DeserializeSchema deserializes a schema.Schema from a serial.Message. 88 func DeserializeSchema(ctx context.Context, nbf *types.NomsBinFormat, v types.Value) (schema.Schema, error) { 89 assertTrue(nbf.UsesFlatbuffers(), "cannot call DeserializeSchema with non-Flatbuffers NomsBinFormat") 90 sm, ok := v.(types.SerialMessage) 91 assertTrue(ok, "must pass types.SerialMessage value to DeserializeSchema") 92 return deserializeSchemaFromFlatbuffer(ctx, sm) 93 } 94 95 func deserializeSchemaFromFlatbuffer(ctx context.Context, buf []byte) (schema.Schema, error) { 96 assertTrue(serial.GetFileID(buf) == serial.TableSchemaFileID, "serialized schema must have FileID == TableSchemaFileID") 97 s, err := serial.TryGetRootAsTableSchema(buf, serial.MessagePrefixSz) 98 if err != nil { 99 return nil, err 100 } 101 102 cols, err := deserializeColumns(ctx, s) 103 if err != nil { 104 return nil, err 105 } 106 sch, err := schema.SchemaFromCols(schema.NewColCollection(cols...)) 107 if err != nil { 108 return nil, err 109 } 110 111 dci, err := deserializeClusteredIndex(s) 112 if err != nil { 113 return nil, err 114 } 115 err = sch.SetPkOrdinals(dci) 116 if err != nil { 117 return nil, err 118 } 119 120 err = deserializeSecondaryIndexes(sch, s) 121 if err != nil { 122 return nil, err 123 } 124 125 err = deserializeChecks(sch, s) 126 if err != nil { 127 return nil, err 128 } 129 130 sch.SetCollation(schema.Collation(s.Collation())) 131 sch.SetComment(string(s.Comment())) 132 133 return sch, nil 134 } 135 136 // clustered indexes 137 138 func serializeClusteredIndex(b *fb.Builder, sch schema.Schema) fb.UOffsetT { 139 keyless := schema.IsKeyless(sch) 140 141 // serialize key columns 142 var ko fb.UOffsetT 143 if keyless { 144 // keyless id is the 2nd to last column 145 // in the columns vector (by convention) 146 // and the only field in key tuples of 147 // the clustered index. 148 idPos := sch.GetAllCols().Size() 149 serial.IndexStartIndexColumnsVector(b, 1) 150 b.PrependUint16(uint16(idPos)) 151 ko = b.EndVector(1) 152 } else { 153 pkMap := sch.GetPkOrdinals() 154 serial.IndexStartIndexColumnsVector(b, len(pkMap)) 155 for i := len(pkMap) - 1; i >= 0; i-- { 156 b.PrependUint16(uint16(pkMap[i])) 157 } 158 ko = b.EndVector(len(pkMap)) 159 } 160 161 // serialize value columns 162 nonPk := sch.GetNonPKCols().GetColumns() 163 length := len(nonPk) 164 if keyless { 165 length++ 166 } 167 serial.IndexStartValueColumnsVector(b, length) 168 for i := len(nonPk) - 1; i >= 0; i-- { 169 col := nonPk[i] 170 pos := sch.GetAllCols().TagToIdx[col.Tag] 171 b.PrependUint16(uint16(pos)) 172 } 173 if keyless { 174 // keyless cardinality is the last column 175 // in the columns vector (by convention) 176 // and the first field in value tuples of 177 // the clustered index. 178 cardPos := sch.GetAllCols().Size() + 1 179 b.PrependUint16(uint16(cardPos)) 180 } 181 vo := b.EndVector(length) 182 183 serial.IndexStart(b) 184 // key_columns == index_columns for clustered index 185 serial.IndexAddIndexColumns(b, ko) 186 serial.IndexAddKeyColumns(b, ko) 187 serial.IndexAddValueColumns(b, vo) 188 serial.IndexAddPrimaryKey(b, true) 189 serial.IndexAddUniqueKey(b, true) 190 serial.IndexAddSpatialKey(b, false) 191 serial.IndexAddSystemDefined(b, false) 192 return serial.IndexEnd(b) 193 } 194 195 func deserializeClusteredIndex(s *serial.TableSchema) ([]int, error) { 196 // check for keyless schema 197 kss, err := keylessSerialSchema(s) 198 if err != nil { 199 return nil, err 200 } 201 if kss { 202 return nil, nil 203 } 204 205 ci, err := s.TryClusteredIndex(nil) 206 if err != nil { 207 return nil, err 208 } 209 pkOrdinals := make([]int, ci.KeyColumnsLength()) 210 for i := range pkOrdinals { 211 pkOrdinals[i] = int(ci.KeyColumns(i)) 212 } 213 return pkOrdinals, nil 214 } 215 216 func serializeSchemaColumns(b *fb.Builder, sch schema.Schema) fb.UOffsetT { 217 cols := sch.GetAllCols().GetColumns() 218 offs := make([]fb.UOffsetT, len(cols)) 219 220 if schema.IsKeyless(sch) { 221 // (6/15/22) 222 // currently, keyless id and cardinality columns 223 // do not exist in schema.Schema 224 // we do serialize them in the flatbuffer 225 // message, in order to describe index storage. 226 // by convention, they are stored as the last 227 // two columns in the columns vector. 228 id, card := serializeHiddenKeylessColumns(b) 229 offs = append(offs, id, card) 230 } 231 232 // serialize columns in |cols| 233 for i := len(cols) - 1; i >= 0; i-- { 234 col := cols[i] 235 var defVal, onUpdateVal string 236 if col.Default != "" { 237 defVal = col.Default 238 } else { 239 defVal = col.Generated 240 } 241 242 if col.OnUpdate != "" { 243 onUpdateVal = col.OnUpdate 244 } 245 246 co := b.CreateString(col.Comment) 247 do := b.CreateString(defVal) 248 ou := b.CreateString(onUpdateVal) 249 250 typeString := sqlTypeString(col.TypeInfo) 251 to := b.CreateString(typeString) 252 no := b.CreateString(col.Name) 253 254 serial.ColumnStart(b) 255 serial.ColumnAddName(b, no) 256 serial.ColumnAddSqlType(b, to) 257 serial.ColumnAddDefaultValue(b, do) 258 serial.ColumnAddComment(b, co) 259 // schema.Schema determines display order 260 serial.ColumnAddDisplayOrder(b, int16(i)) 261 serial.ColumnAddTag(b, col.Tag) 262 serial.ColumnAddEncoding(b, encodingFromTypeinfo(col.TypeInfo)) 263 serial.ColumnAddPrimaryKey(b, col.IsPartOfPK) 264 serial.ColumnAddAutoIncrement(b, col.AutoIncrement) 265 serial.ColumnAddNullable(b, col.IsNullable()) 266 serial.ColumnAddGenerated(b, col.Generated != "") 267 serial.ColumnAddVirtual(b, col.Virtual) 268 if onUpdateVal != "" { 269 serial.ColumnAddOnUpdateValue(b, ou) 270 } 271 serial.ColumnAddHidden(b, false) 272 offs[i] = serial.ColumnEnd(b) 273 } 274 275 // create the columns array with all columns 276 serial.TableSchemaStartColumnsVector(b, len(offs)) 277 for i := len(offs) - 1; i >= 0; i-- { 278 b.PrependUOffsetT(offs[i]) 279 } 280 return b.EndVector(len(offs)) 281 } 282 283 func serializeHiddenKeylessColumns(b *fb.Builder) (id, card fb.UOffsetT) { 284 // cardinality column 285 no := b.CreateString(keylessCardCol) 286 serial.ColumnStart(b) 287 serial.ColumnAddName(b, no) 288 serial.ColumnAddDisplayOrder(b, int16(-1)) 289 serial.ColumnAddTag(b, schema.KeylessRowCardinalityTag) 290 serial.ColumnAddEncoding(b, serial.EncodingUint64) 291 // set hidden and generated to true 292 serial.ColumnAddGenerated(b, true) 293 serial.ColumnAddHidden(b, true) 294 serial.ColumnAddPrimaryKey(b, false) 295 serial.ColumnAddAutoIncrement(b, false) 296 serial.ColumnAddNullable(b, false) 297 serial.ColumnAddVirtual(b, false) 298 card = serial.ColumnEnd(b) 299 300 // hash id column 301 no = b.CreateString(keylessIdCol) 302 serial.ColumnStart(b) 303 serial.ColumnAddName(b, no) 304 serial.ColumnAddDisplayOrder(b, int16(-1)) 305 serial.ColumnAddTag(b, schema.KeylessRowIdTag) 306 serial.ColumnAddEncoding(b, serial.EncodingHash128) 307 // set hidden and generated to true 308 serial.ColumnAddGenerated(b, true) 309 serial.ColumnAddHidden(b, true) 310 serial.ColumnAddPrimaryKey(b, false) 311 serial.ColumnAddAutoIncrement(b, false) 312 serial.ColumnAddNullable(b, false) 313 serial.ColumnAddVirtual(b, false) 314 id = serial.ColumnEnd(b) 315 316 return 317 } 318 319 func deserializeColumns(ctx context.Context, s *serial.TableSchema) ([]schema.Column, error) { 320 length := s.ColumnsLength() 321 isKeyless, err := keylessSerialSchema(s) 322 if err != nil { 323 return nil, err 324 } 325 if isKeyless { 326 // (6/15/22) 327 // currently, keyless id and cardinality columns 328 // do not exist in schema.Schema 329 // we do serialize them in the flatbuffer 330 // message, in order to describe index storage. 331 // by convention, they are stored as the last 332 // two columns in the columns vector. 333 length -= 2 334 } 335 336 cols := make([]schema.Column, length) 337 c := serial.Column{} 338 for i := range cols { 339 _, err := s.TryColumns(&c, i) 340 if err != nil { 341 return nil, err 342 } 343 sqlType, err := typeinfoFromSqlType(string(c.SqlType())) 344 if err != nil { 345 return nil, err 346 } 347 348 var defVal, generatedVal, onUpdateVal string 349 if c.DefaultValue() != nil { 350 if c.Generated() { 351 generatedVal = string(c.DefaultValue()) 352 } else { 353 defVal = string(c.DefaultValue()) 354 } 355 } 356 357 if c.OnUpdateValue() != nil { 358 onUpdateVal = string(c.OnUpdateValue()) 359 } 360 361 cols[i] = schema.Column{ 362 Name: string(c.Name()), 363 Tag: c.Tag(), 364 Kind: sqlType.NomsKind(), 365 IsPartOfPK: c.PrimaryKey(), 366 TypeInfo: sqlType, 367 Default: defVal, 368 Generated: generatedVal, 369 OnUpdate: onUpdateVal, 370 Virtual: c.Virtual(), 371 AutoIncrement: c.AutoIncrement(), 372 Comment: string(c.Comment()), 373 Constraints: constraintsFromSerialColumn(&c), 374 } 375 } 376 return cols, nil 377 } 378 379 func serializeSecondaryIndexes(b *fb.Builder, sch schema.Schema, indexes []schema.Index) fb.UOffsetT { 380 ordinalMap := sch.GetAllCols().TagToIdx 381 offs := make([]fb.UOffsetT, len(indexes)) 382 for i := len(offs) - 1; i >= 0; i-- { 383 idx := indexes[i] 384 no := b.CreateString(idx.Name()) 385 co := b.CreateString(idx.Comment()) 386 387 // serialize indexed columns 388 tags := idx.IndexedColumnTags() 389 serial.IndexStartIndexColumnsVector(b, len(tags)) 390 for j := len(tags) - 1; j >= 0; j-- { 391 pos := ordinalMap[tags[j]] 392 b.PrependUint16(uint16(pos)) 393 } 394 ico := b.EndVector(len(tags)) 395 396 // serialize key columns 397 tags = idx.AllTags() 398 serial.IndexStartKeyColumnsVector(b, len(tags)) 399 for j := len(tags) - 1; j >= 0; j-- { 400 pos := ordinalMap[tags[j]] 401 b.PrependUint16(uint16(pos)) 402 } 403 ko := b.EndVector(len(tags)) 404 405 // serialize prefix lengths 406 prefixLengths := idx.PrefixLengths() 407 serial.IndexStartPrefixLengthsVector(b, len(prefixLengths)) 408 for j := len(prefixLengths) - 1; j >= 0; j-- { 409 b.PrependUint16(prefixLengths[j]) 410 } 411 po := b.EndVector(len(prefixLengths)) 412 413 var ftInfo fb.UOffsetT 414 if idx.IsFullText() { 415 ftInfo = serializeFullTextInfo(b, idx) 416 } 417 418 serial.IndexStart(b) 419 serial.IndexAddName(b, no) 420 serial.IndexAddComment(b, co) 421 serial.IndexAddIndexColumns(b, ico) 422 serial.IndexAddKeyColumns(b, ko) 423 serial.IndexAddPrimaryKey(b, false) 424 serial.IndexAddUniqueKey(b, idx.IsUnique()) 425 serial.IndexAddSystemDefined(b, !idx.IsUserDefined()) 426 serial.IndexAddPrefixLengths(b, po) 427 serial.IndexAddSpatialKey(b, idx.IsSpatial()) 428 serial.IndexAddFulltextKey(b, idx.IsFullText()) 429 if idx.IsFullText() { 430 serial.IndexAddFulltextInfo(b, ftInfo) 431 } 432 offs[i] = serial.IndexEnd(b) 433 } 434 435 serial.TableSchemaStartSecondaryIndexesVector(b, len(indexes)) 436 for i := len(offs) - 1; i >= 0; i-- { 437 b.PrependUOffsetT(offs[i]) 438 } 439 return b.EndVector(len(indexes)) 440 } 441 442 func deserializeSecondaryIndexes(sch schema.Schema, s *serial.TableSchema) error { 443 idx := serial.Index{} 444 col := serial.Column{} 445 for i := 0; i < s.SecondaryIndexesLength(); i++ { 446 _, err := s.TrySecondaryIndexes(&idx, i) 447 if err != nil { 448 return err 449 } 450 assertTrue(!idx.PrimaryKey(), "cannot deserialize secondary index with PrimaryKey() == true") 451 452 fti, err := deserializeFullTextInfo(&idx) 453 if err != nil { 454 return err 455 } 456 457 name := string(idx.Name()) 458 props := schema.IndexProperties{ 459 IsUnique: idx.UniqueKey(), 460 IsSpatial: idx.SpatialKey(), 461 IsFullText: idx.FulltextKey(), 462 IsUserDefined: !idx.SystemDefined(), 463 Comment: string(idx.Comment()), 464 FullTextProperties: fti, 465 } 466 467 tags := make([]uint64, idx.IndexColumnsLength()) 468 for j := range tags { 469 pos := idx.IndexColumns(j) 470 _, err := s.TryColumns(&col, int(pos)) 471 if err != nil { 472 return err 473 } 474 tags[j] = col.Tag() 475 } 476 477 var prefixLengths []uint16 478 prefixLengthsLength := idx.PrefixLengthsLength() 479 if prefixLengthsLength > 0 { 480 prefixLengths = make([]uint16, prefixLengthsLength) 481 for j := range prefixLengths { 482 prefixLengths[j] = idx.PrefixLengths(j) 483 } 484 } 485 486 _, err = sch.Indexes().AddIndexByColTags(name, tags, prefixLengths, props) 487 if err != nil { 488 return err 489 } 490 } 491 return nil 492 } 493 494 func serializeChecks(b *fb.Builder, checks []schema.Check) fb.UOffsetT { 495 offs := make([]fb.UOffsetT, len(checks)) 496 for i := len(offs) - 1; i >= 0; i-- { 497 eo := b.CreateString(checks[i].Expression()) 498 no := b.CreateString(checks[i].Name()) 499 serial.CheckConstraintStart(b) 500 serial.CheckConstraintAddEnforced(b, checks[i].Enforced()) 501 serial.CheckConstraintAddExpression(b, eo) 502 serial.CheckConstraintAddName(b, no) 503 offs[i] = serial.CheckConstraintEnd(b) 504 } 505 506 serial.TableSchemaStartChecksVector(b, len(checks)) 507 for i := len(offs) - 1; i >= 0; i-- { 508 b.PrependUOffsetT(offs[i]) 509 } 510 return b.EndVector(len(checks)) 511 } 512 513 func deserializeChecks(sch schema.Schema, s *serial.TableSchema) error { 514 coll := sch.Checks() 515 c := serial.CheckConstraint{} 516 for i := 0; i < s.ChecksLength(); i++ { 517 _, err := s.TryChecks(&c, i) 518 if err != nil { 519 return err 520 } 521 n, e := string(c.Name()), string(c.Expression()) 522 if _, err := coll.AddCheck(n, e, c.Enforced()); err != nil { 523 return err 524 } 525 } 526 return nil 527 } 528 529 func serializeFullTextInfo(b *fb.Builder, idx schema.Index) fb.UOffsetT { 530 props := idx.FullTextProperties() 531 532 configTable := b.CreateString(props.ConfigTable) 533 posTable := b.CreateString(props.PositionTable) 534 docCountTable := b.CreateString(props.DocCountTable) 535 globalCountTable := b.CreateString(props.GlobalCountTable) 536 rowCountTable := b.CreateString(props.RowCountTable) 537 keyName := b.CreateString(props.KeyName) 538 539 keyPositions := idx.FullTextProperties().KeyPositions 540 serial.FulltextInfoStartKeyPositionsVector(b, len(keyPositions)) 541 for j := len(keyPositions) - 1; j >= 0; j-- { 542 b.PrependUint16(keyPositions[j]) 543 } 544 keyPos := b.EndVector(len(keyPositions)) 545 546 serial.FulltextInfoStart(b) 547 serial.FulltextInfoAddConfigTable(b, configTable) 548 serial.FulltextInfoAddPositionTable(b, posTable) 549 serial.FulltextInfoAddDocCountTable(b, docCountTable) 550 serial.FulltextInfoAddGlobalCountTable(b, globalCountTable) 551 serial.FulltextInfoAddRowCountTable(b, rowCountTable) 552 serial.FulltextInfoAddKeyType(b, props.KeyType) 553 serial.FulltextInfoAddKeyName(b, keyName) 554 serial.FulltextInfoAddKeyPositions(b, keyPos) 555 return serial.FulltextInfoEnd(b) 556 } 557 558 func deserializeFullTextInfo(idx *serial.Index) (schema.FullTextProperties, error) { 559 fulltext := serial.FulltextInfo{} 560 has, err := idx.TryFulltextInfo(&fulltext) 561 if err != nil { 562 return schema.FullTextProperties{}, err 563 } 564 if has == nil { 565 return schema.FullTextProperties{}, nil 566 } 567 568 var keyPositions []uint16 569 keyPositionsLength := fulltext.KeyPositionsLength() 570 if keyPositionsLength > 0 { 571 keyPositions = make([]uint16, keyPositionsLength) 572 for j := range keyPositions { 573 keyPositions[j] = fulltext.KeyPositions(j) 574 } 575 } 576 577 return schema.FullTextProperties{ 578 ConfigTable: string(fulltext.ConfigTable()), 579 PositionTable: string(fulltext.PositionTable()), 580 DocCountTable: string(fulltext.DocCountTable()), 581 GlobalCountTable: string(fulltext.GlobalCountTable()), 582 RowCountTable: string(fulltext.RowCountTable()), 583 KeyType: fulltext.KeyType(), 584 KeyName: string(fulltext.KeyName()), 585 KeyPositions: keyPositions, 586 }, nil 587 } 588 589 func keylessSerialSchema(s *serial.TableSchema) (bool, error) { 590 n := s.ColumnsLength() 591 if n < 2 { 592 return false, nil 593 } 594 // keyless id is the 2nd to last column 595 // in the columns vector (by convention) 596 // and the only field in key tuples of 597 // the clustered index. 598 id := serial.Column{} 599 _, err := s.TryColumns(&id, n-2) 600 if err != nil { 601 return false, err 602 } 603 ok := id.Generated() && id.Hidden() && 604 string(id.Name()) == keylessIdCol 605 if !ok { 606 return false, nil 607 } 608 609 // keyless cardinality is the last column 610 // in the columns vector (by convention) 611 // and the first field in value tuples of 612 // the clustered index. 613 card := serial.Column{} 614 _, err = s.TryColumns(&card, n-1) 615 if err != nil { 616 return false, err 617 } 618 return card.Generated() && card.Hidden() && 619 string(card.Name()) == keylessCardCol, nil 620 } 621 622 func sqlTypeString(t typeinfo.TypeInfo) string { 623 typ := t.ToSqlType() 624 if st, ok := typ.(sql.SpatialColumnType); ok { 625 // for spatial types, we must append the SRID 626 if srid, ok := st.GetSpatialTypeSRID(); ok { 627 return fmt.Sprintf("%s SRID %d", typ.String(), srid) 628 } 629 } 630 631 // For datetime types, always store the precision explicitly so that it can be read back precisely, although MySQL 632 // omits the precision when it's 0 (the default). 633 if sqltypes.IsDatetimeType(typ) || sqltypes.IsTimestampType(typ) { 634 dt := typ.(sql.DatetimeType) 635 if dt.Precision() == 0 { 636 return fmt.Sprintf("%s(0)", typ.String()) 637 } 638 return typ.String() 639 } 640 641 // Extended types are string serializable, so we'll just prepend a tag 642 if extendedType, ok := typ.(sqltypes.ExtendedType); ok { 643 serializedType, err := sqltypes.SerializeTypeToString(extendedType) 644 if err != nil { 645 panic(err) 646 } 647 return planbuilder.ExtendedTypeTag + serializedType 648 } 649 650 return typ.String() 651 } 652 653 func typeinfoFromSqlType(s string) (typeinfo.TypeInfo, error) { 654 sqlType, err := planbuilder.ParseColumnTypeString(s) 655 if err != nil { 656 return nil, err 657 } 658 return typeinfo.FromSqlType(sqlType) 659 } 660 661 func encodingFromTypeinfo(t typeinfo.TypeInfo) serial.Encoding { 662 return schema.EncodingFromSqlType(t.ToSqlType()) 663 } 664 665 func constraintsFromSerialColumn(col *serial.Column) (cc []schema.ColConstraint) { 666 if !col.Nullable() || col.PrimaryKey() { 667 cc = append(cc, schema.NotNullConstraint{}) 668 } 669 return 670 } 671 672 func assertTrue(b bool, msg string) { 673 if !b { 674 panic("assertion failed: " + msg) 675 } 676 }