github.com/seashell-org/golang-migrate/v4@v4.15.3-0.20220722221203-6ab6c6c062d1/database/spanner/spansql/sql.go (about) 1 /* 2 Copyright 2019 Google LLC 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package spansql 18 19 // This file holds SQL methods for rendering the types in types.go 20 // as the SQL dialect that this package parses. 21 // 22 // Every exported type has an SQL method that returns a string. 23 // Some also have an addSQL method that efficiently builds that string 24 // in a provided strings.Builder. 25 26 import ( 27 "fmt" 28 "sort" 29 "strconv" 30 "strings" 31 "time" 32 ) 33 34 func buildSQL(x interface{ addSQL(*strings.Builder) }) string { 35 var sb strings.Builder 36 x.addSQL(&sb) 37 return sb.String() 38 } 39 40 func (ct CreateTable) SQL() string { 41 str := "CREATE TABLE " + ct.Name.SQL() + " (\n" 42 for _, c := range ct.Columns { 43 str += " " + c.SQL() + ",\n" 44 } 45 for _, tc := range ct.Constraints { 46 str += " " + tc.SQL() + ",\n" 47 } 48 str += ") PRIMARY KEY(" 49 for i, c := range ct.PrimaryKey { 50 if i > 0 { 51 str += ", " 52 } 53 str += c.SQL() 54 } 55 str += ")" 56 if il := ct.Interleave; il != nil { 57 str += ",\n INTERLEAVE IN PARENT " + il.Parent.SQL() + " ON DELETE " + il.OnDelete.SQL() 58 } 59 if rdp := ct.RowDeletionPolicy; rdp != nil { 60 str += ",\n " + rdp.SQL() 61 } 62 return str 63 } 64 65 func (ci CreateIndex) SQL() string { 66 str := "CREATE" 67 if ci.Unique { 68 str += " UNIQUE" 69 } 70 if ci.NullFiltered { 71 str += " NULL_FILTERED" 72 } 73 str += " INDEX " + ci.Name.SQL() + " ON " + ci.Table.SQL() + "(" 74 for i, c := range ci.Columns { 75 if i > 0 { 76 str += ", " 77 } 78 str += c.SQL() 79 } 80 str += ")" 81 if len(ci.Storing) > 0 { 82 str += " STORING (" + idList(ci.Storing, ", ") + ")" 83 } 84 if ci.Interleave != "" { 85 str += ", INTERLEAVE IN " + ci.Interleave.SQL() 86 } 87 return str 88 } 89 90 func (cv CreateView) SQL() string { 91 str := "CREATE" 92 if cv.OrReplace { 93 str += " OR REPLACE" 94 } 95 str += " VIEW " + cv.Name.SQL() + " SQL SECURITY INVOKER AS " + cv.Query.SQL() 96 return str 97 } 98 99 func (dt DropTable) SQL() string { 100 return "DROP TABLE " + dt.Name.SQL() 101 } 102 103 func (di DropIndex) SQL() string { 104 return "DROP INDEX " + di.Name.SQL() 105 } 106 107 func (dv DropView) SQL() string { 108 return "DROP VIEW " + dv.Name.SQL() 109 } 110 111 func (at AlterTable) SQL() string { 112 return "ALTER TABLE " + at.Name.SQL() + " " + at.Alteration.SQL() 113 } 114 115 func (ac AddColumn) SQL() string { 116 return "ADD COLUMN " + ac.Def.SQL() 117 } 118 119 func (dc DropColumn) SQL() string { 120 return "DROP COLUMN " + dc.Name.SQL() 121 } 122 123 func (ac AddConstraint) SQL() string { 124 return "ADD " + ac.Constraint.SQL() 125 } 126 127 func (dc DropConstraint) SQL() string { 128 return "DROP CONSTRAINT " + dc.Name.SQL() 129 } 130 131 func (sod SetOnDelete) SQL() string { 132 return "SET ON DELETE " + sod.Action.SQL() 133 } 134 135 func (od OnDelete) SQL() string { 136 switch od { 137 case NoActionOnDelete: 138 return "NO ACTION" 139 case CascadeOnDelete: 140 return "CASCADE" 141 } 142 panic("unknown OnDelete") 143 } 144 145 func (ac AlterColumn) SQL() string { 146 return "ALTER COLUMN " + ac.Name.SQL() + " " + ac.Alteration.SQL() 147 } 148 149 func (ardp AddRowDeletionPolicy) SQL() string { 150 return "ADD " + ardp.RowDeletionPolicy.SQL() 151 } 152 153 func (rrdp ReplaceRowDeletionPolicy) SQL() string { 154 return "REPLACE " + rrdp.RowDeletionPolicy.SQL() 155 } 156 157 func (drdp DropRowDeletionPolicy) SQL() string { 158 return "DROP ROW DELETION POLICY" 159 } 160 161 func (sct SetColumnType) SQL() string { 162 str := sct.Type.SQL() 163 if sct.NotNull { 164 str += " NOT NULL" 165 } 166 if sct.Default != nil { 167 str += " DEFAULT (" + sct.Default.SQL() + ")" 168 } 169 return str 170 } 171 172 func (sco SetColumnOptions) SQL() string { 173 // TODO: not clear what to do for no options. 174 return "SET " + sco.Options.SQL() 175 } 176 177 func (sd SetDefault) SQL() string { 178 return "SET DEFAULT (" + sd.Default.SQL() + ")" 179 } 180 181 func (dp DropDefault) SQL() string { 182 return "DROP DEFAULT" 183 } 184 185 func (co ColumnOptions) SQL() string { 186 str := "OPTIONS (" 187 if co.AllowCommitTimestamp != nil { 188 if *co.AllowCommitTimestamp { 189 str += "allow_commit_timestamp = true" 190 } else { 191 str += "allow_commit_timestamp = null" 192 } 193 } 194 str += ")" 195 return str 196 } 197 198 func (ad AlterDatabase) SQL() string { 199 return "ALTER DATABASE " + ad.Name.SQL() + " " + ad.Alteration.SQL() 200 } 201 202 func (sdo SetDatabaseOptions) SQL() string { 203 return "SET " + sdo.Options.SQL() 204 } 205 206 func (do DatabaseOptions) SQL() string { 207 str := "OPTIONS (" 208 hasOpt := false 209 if do.OptimizerVersion != nil { 210 hasOpt = true 211 if *do.OptimizerVersion == 0 { 212 str += "optimizer_version=null" 213 } else { 214 str += fmt.Sprintf("optimizer_version=%v", *do.OptimizerVersion) 215 } 216 } 217 if do.VersionRetentionPeriod != nil { 218 if hasOpt { 219 str += ", " 220 } 221 hasOpt = true 222 if *do.VersionRetentionPeriod == "" { 223 str += "version_retention_period=null" 224 } else { 225 str += fmt.Sprintf("version_retention_period='%s'", *do.VersionRetentionPeriod) 226 } 227 } 228 if do.EnableKeyVisualizer != nil { 229 if hasOpt { 230 str += ", " 231 } 232 hasOpt = true 233 if *do.EnableKeyVisualizer { 234 str += "enable_key_visualizer=true" 235 } else { 236 str += "enable_key_visualizer=null" 237 } 238 } 239 str += ")" 240 return str 241 } 242 243 func (d *Delete) SQL() string { 244 return "DELETE FROM " + d.Table.SQL() + " WHERE " + d.Where.SQL() 245 } 246 247 func (u *Update) SQL() string { 248 str := "UPDATE " + u.Table.SQL() + " SET " 249 for i, item := range u.Items { 250 if i > 0 { 251 str += ", " 252 } 253 str += item.Column.SQL() + " = " 254 if item.Value != nil { 255 str += item.Value.SQL() 256 } else { 257 str += "DEFAULT" 258 } 259 } 260 str += " WHERE " + u.Where.SQL() 261 return str 262 } 263 264 func (i *Insert) SQL() string { 265 str := "INSERT INTO " + i.Table.SQL() + " (" 266 for i, column := range i.Columns { 267 if i > 0 { 268 str += ", " 269 } 270 str += column.SQL() 271 } 272 str += ") " 273 str += i.Input.SQL() 274 return str 275 } 276 277 func (v Values) SQL() string { 278 str := "VALUES " 279 for j, values := range v { 280 if j > 0 { 281 str += ", " 282 } 283 str += "(" 284 285 for k, value := range values { 286 if k > 0 { 287 str += ", " 288 } 289 str += value.SQL() 290 } 291 str += ")" 292 } 293 return str 294 } 295 296 func (cd ColumnDef) SQL() string { 297 str := cd.Name.SQL() + " " + cd.Type.SQL() 298 if cd.NotNull { 299 str += " NOT NULL" 300 } 301 if cd.Default != nil { 302 str += " DEFAULT (" + cd.Default.SQL() + ")" 303 } 304 if cd.Generated != nil { 305 str += " AS (" + cd.Generated.SQL() + ") STORED" 306 } 307 if cd.Options != (ColumnOptions{}) { 308 str += " " + cd.Options.SQL() 309 } 310 return str 311 } 312 313 func (tc TableConstraint) SQL() string { 314 var str string 315 if tc.Name != "" { 316 str += "CONSTRAINT " + tc.Name.SQL() + " " 317 } 318 str += tc.Constraint.SQL() 319 return str 320 } 321 322 func (rdp RowDeletionPolicy) SQL() string { 323 return "ROW DELETION POLICY ( OLDER_THAN ( " + rdp.Column.SQL() + ", INTERVAL " + strconv.FormatInt(rdp.NumDays, 10) + " DAY ))" 324 } 325 326 func (fk ForeignKey) SQL() string { 327 str := "FOREIGN KEY (" + idList(fk.Columns, ", ") 328 str += ") REFERENCES " + fk.RefTable.SQL() + " (" 329 str += idList(fk.RefColumns, ", ") + ")" 330 return str 331 } 332 333 func (c Check) SQL() string { 334 return "CHECK (" + c.Expr.SQL() + ")" 335 } 336 337 func (t Type) SQL() string { 338 str := t.Base.SQL() 339 if t.Len > 0 && (t.Base == String || t.Base == Bytes) { 340 str += "(" 341 if t.Len == MaxLen { 342 str += "MAX" 343 } else { 344 str += strconv.FormatInt(t.Len, 10) 345 } 346 str += ")" 347 } 348 if t.Array { 349 str = "ARRAY<" + str + ">" 350 } 351 return str 352 } 353 354 func (tb TypeBase) SQL() string { 355 switch tb { 356 case Bool: 357 return "BOOL" 358 case Int64: 359 return "INT64" 360 case Float64: 361 return "FLOAT64" 362 case Numeric: 363 return "NUMERIC" 364 case String: 365 return "STRING" 366 case Bytes: 367 return "BYTES" 368 case Date: 369 return "DATE" 370 case Timestamp: 371 return "TIMESTAMP" 372 case JSON: 373 return "JSON" 374 } 375 panic("unknown TypeBase") 376 } 377 378 func (kp KeyPart) SQL() string { 379 str := kp.Column.SQL() 380 if kp.Desc { 381 str += " DESC" 382 } 383 return str 384 } 385 386 func (q Query) SQL() string { return buildSQL(q) } 387 func (q Query) addSQL(sb *strings.Builder) { 388 q.Select.addSQL(sb) 389 if len(q.Order) > 0 { 390 sb.WriteString(" ORDER BY ") 391 for i, o := range q.Order { 392 if i > 0 { 393 sb.WriteString(", ") 394 } 395 o.addSQL(sb) 396 } 397 } 398 if q.Limit != nil { 399 sb.WriteString(" LIMIT ") 400 sb.WriteString(q.Limit.SQL()) 401 if q.Offset != nil { 402 sb.WriteString(" OFFSET ") 403 sb.WriteString(q.Offset.SQL()) 404 } 405 } 406 } 407 408 func (sel Select) SQL() string { return buildSQL(sel) } 409 func (sel Select) addSQL(sb *strings.Builder) { 410 sb.WriteString("SELECT ") 411 if sel.Distinct { 412 sb.WriteString("DISTINCT ") 413 } 414 for i, e := range sel.List { 415 if i > 0 { 416 sb.WriteString(", ") 417 } 418 e.addSQL(sb) 419 if len(sel.ListAliases) > 0 { 420 alias := sel.ListAliases[i] 421 if alias != "" { 422 sb.WriteString(" AS ") 423 sb.WriteString(alias.SQL()) 424 } 425 } 426 } 427 if len(sel.From) > 0 { 428 sb.WriteString(" FROM ") 429 for i, f := range sel.From { 430 if i > 0 { 431 sb.WriteString(", ") 432 } 433 sb.WriteString(f.SQL()) 434 } 435 } 436 if sel.Where != nil { 437 sb.WriteString(" WHERE ") 438 sel.Where.addSQL(sb) 439 } 440 if len(sel.GroupBy) > 0 { 441 sb.WriteString(" GROUP BY ") 442 addExprList(sb, sel.GroupBy, ", ") 443 } 444 } 445 446 func (sft SelectFromTable) SQL() string { 447 str := sft.Table.SQL() 448 if len(sft.Hints) > 0 { 449 str += "@{" 450 kvs := make([]string, len(sft.Hints)) 451 i := 0 452 for k, v := range sft.Hints { 453 kvs[i] = fmt.Sprintf("%s=%s", k, v) 454 i++ 455 } 456 sort.Strings(kvs) 457 str += strings.Join(kvs, ",") 458 str += "}" 459 } 460 461 if sft.Alias != "" { 462 str += " AS " + sft.Alias.SQL() 463 } 464 return str 465 } 466 467 func (sfj SelectFromJoin) SQL() string { 468 // TODO: The grammar permits arbitrary nesting. Does this need to add parens? 469 str := sfj.LHS.SQL() + " " + joinTypes[sfj.Type] + " JOIN " 470 // TODO: hints go here 471 str += sfj.RHS.SQL() 472 if sfj.On != nil { 473 str += " ON " + sfj.On.SQL() 474 } else if len(sfj.Using) > 0 { 475 str += " USING (" + idList(sfj.Using, ", ") + ")" 476 } 477 return str 478 } 479 480 var joinTypes = map[JoinType]string{ 481 InnerJoin: "INNER", 482 CrossJoin: "CROSS", 483 FullJoin: "FULL", 484 LeftJoin: "LEFT", 485 RightJoin: "RIGHT", 486 } 487 488 func (sfu SelectFromUnnest) SQL() string { 489 str := "UNNEST(" + sfu.Expr.SQL() + ")" 490 if sfu.Alias != "" { 491 str += " AS " + sfu.Alias.SQL() 492 } 493 return str 494 } 495 496 func (o Order) SQL() string { return buildSQL(o) } 497 func (o Order) addSQL(sb *strings.Builder) { 498 o.Expr.addSQL(sb) 499 if o.Desc { 500 sb.WriteString(" DESC") 501 } 502 } 503 504 var arithOps = map[ArithOperator]string{ 505 // Binary operators only; unary operators are handled first. 506 Mul: "*", 507 Div: "/", 508 Concat: "||", 509 Add: "+", 510 Sub: "-", 511 BitShl: "<<", 512 BitShr: ">>", 513 BitAnd: "&", 514 BitXor: "^", 515 BitOr: "|", 516 } 517 518 func (ao ArithOp) SQL() string { return buildSQL(ao) } 519 func (ao ArithOp) addSQL(sb *strings.Builder) { 520 // Extra parens inserted to ensure the correct precedence. 521 522 switch ao.Op { 523 case Neg: 524 sb.WriteString("-(") 525 ao.RHS.addSQL(sb) 526 sb.WriteString(")") 527 return 528 case Plus: 529 sb.WriteString("+(") 530 ao.RHS.addSQL(sb) 531 sb.WriteString(")") 532 return 533 case BitNot: 534 sb.WriteString("~(") 535 ao.RHS.addSQL(sb) 536 sb.WriteString(")") 537 return 538 } 539 op, ok := arithOps[ao.Op] 540 if !ok { 541 panic("unknown ArithOp") 542 } 543 sb.WriteString("(") 544 ao.LHS.addSQL(sb) 545 sb.WriteString(")") 546 sb.WriteString(op) 547 sb.WriteString("(") 548 ao.RHS.addSQL(sb) 549 sb.WriteString(")") 550 } 551 552 func (lo LogicalOp) SQL() string { return buildSQL(lo) } 553 func (lo LogicalOp) addSQL(sb *strings.Builder) { 554 switch lo.Op { 555 default: 556 panic("unknown LogicalOp") 557 case And: 558 lo.LHS.addSQL(sb) 559 sb.WriteString(" AND ") 560 case Or: 561 lo.LHS.addSQL(sb) 562 sb.WriteString(" OR ") 563 case Not: 564 sb.WriteString("NOT ") 565 } 566 lo.RHS.addSQL(sb) 567 } 568 569 var compOps = map[ComparisonOperator]string{ 570 Lt: "<", 571 Le: "<=", 572 Gt: ">", 573 Ge: ">=", 574 Eq: "=", 575 Ne: "!=", 576 Like: "LIKE", 577 NotLike: "NOT LIKE", 578 Between: "BETWEEN", 579 NotBetween: "NOT BETWEEN", 580 } 581 582 func (co ComparisonOp) SQL() string { return buildSQL(co) } 583 func (co ComparisonOp) addSQL(sb *strings.Builder) { 584 op, ok := compOps[co.Op] 585 if !ok { 586 panic("unknown ComparisonOp") 587 } 588 co.LHS.addSQL(sb) 589 sb.WriteString(" ") 590 sb.WriteString(op) 591 sb.WriteString(" ") 592 co.RHS.addSQL(sb) 593 if co.Op == Between || co.Op == NotBetween { 594 sb.WriteString(" AND ") 595 co.RHS2.addSQL(sb) 596 } 597 } 598 599 func (io InOp) SQL() string { return buildSQL(io) } 600 func (io InOp) addSQL(sb *strings.Builder) { 601 io.LHS.addSQL(sb) 602 if io.Neg { 603 sb.WriteString(" NOT") 604 } 605 sb.WriteString(" IN ") 606 if io.Unnest { 607 sb.WriteString("UNNEST") 608 } 609 sb.WriteString("(") 610 addExprList(sb, io.RHS, ", ") 611 sb.WriteString(")") 612 } 613 614 func (io IsOp) SQL() string { return buildSQL(io) } 615 func (io IsOp) addSQL(sb *strings.Builder) { 616 io.LHS.addSQL(sb) 617 sb.WriteString(" IS ") 618 if io.Neg { 619 sb.WriteString("NOT ") 620 } 621 io.RHS.addSQL(sb) 622 } 623 624 func (f Func) SQL() string { return buildSQL(f) } 625 func (f Func) addSQL(sb *strings.Builder) { 626 sb.WriteString(f.Name) 627 sb.WriteString("(") 628 addExprList(sb, f.Args, ", ") 629 sb.WriteString(")") 630 } 631 632 func (te TypedExpr) SQL() string { return buildSQL(te) } 633 func (te TypedExpr) addSQL(sb *strings.Builder) { 634 te.Expr.addSQL(sb) 635 sb.WriteString(" AS ") 636 sb.WriteString(te.Type.SQL()) 637 } 638 639 func (ee ExtractExpr) SQL() string { return buildSQL(ee) } 640 func (ee ExtractExpr) addSQL(sb *strings.Builder) { 641 sb.WriteString(ee.Part) 642 sb.WriteString(" FROM ") 643 ee.Expr.addSQL(sb) 644 } 645 646 func (aze AtTimeZoneExpr) SQL() string { return buildSQL(aze) } 647 func (aze AtTimeZoneExpr) addSQL(sb *strings.Builder) { 648 aze.Expr.addSQL(sb) 649 sb.WriteString(" AT TIME ZONE ") 650 sb.WriteString(aze.Zone) 651 } 652 653 func idList(l []ID, join string) string { 654 var ss []string 655 for _, s := range l { 656 ss = append(ss, s.SQL()) 657 } 658 return strings.Join(ss, join) 659 } 660 661 func addExprList(sb *strings.Builder, l []Expr, join string) { 662 for i, s := range l { 663 if i > 0 { 664 sb.WriteString(join) 665 } 666 s.addSQL(sb) 667 } 668 } 669 670 func addIDList(sb *strings.Builder, l []ID, join string) { 671 for i, s := range l { 672 if i > 0 { 673 sb.WriteString(join) 674 } 675 s.addSQL(sb) 676 } 677 } 678 679 func (pe PathExp) SQL() string { return buildSQL(pe) } 680 func (pe PathExp) addSQL(sb *strings.Builder) { 681 addIDList(sb, []ID(pe), ".") 682 } 683 684 func (p Paren) SQL() string { return buildSQL(p) } 685 func (p Paren) addSQL(sb *strings.Builder) { 686 sb.WriteString("(") 687 p.Expr.addSQL(sb) 688 sb.WriteString(")") 689 } 690 691 func (a Array) SQL() string { return buildSQL(a) } 692 func (a Array) addSQL(sb *strings.Builder) { 693 sb.WriteString("[") 694 addExprList(sb, []Expr(a), ", ") 695 sb.WriteString("]") 696 } 697 698 func (id ID) SQL() string { return buildSQL(id) } 699 func (id ID) addSQL(sb *strings.Builder) { 700 // https://cloud.google.com/spanner/docs/lexical#identifiers 701 702 // TODO: If there are non-letters/numbers/underscores then this also needs quoting. 703 704 if IsKeyword(string(id)) { 705 // TODO: Escaping may be needed here. 706 sb.WriteString("`") 707 sb.WriteString(string(id)) 708 sb.WriteString("`") 709 return 710 } 711 712 sb.WriteString(string(id)) 713 } 714 715 func (p Param) SQL() string { return buildSQL(p) } 716 func (p Param) addSQL(sb *strings.Builder) { 717 sb.WriteString("@") 718 sb.WriteString(string(p)) 719 } 720 721 func (c Case) SQL() string { return buildSQL(c) } 722 func (c Case) addSQL(sb *strings.Builder) { 723 sb.WriteString("CASE ") 724 if c.Expr != nil { 725 fmt.Fprintf(sb, "%s ", c.Expr.SQL()) 726 } 727 for _, w := range c.WhenClauses { 728 fmt.Fprintf(sb, "WHEN %s THEN %s ", w.Cond.SQL(), w.Result.SQL()) 729 } 730 if c.ElseResult != nil { 731 fmt.Fprintf(sb, "ELSE %s ", c.ElseResult.SQL()) 732 } 733 sb.WriteString("END") 734 } 735 736 func (b BoolLiteral) SQL() string { return buildSQL(b) } 737 func (b BoolLiteral) addSQL(sb *strings.Builder) { 738 if b { 739 sb.WriteString("TRUE") 740 } else { 741 sb.WriteString("FALSE") 742 } 743 } 744 745 func (NullLiteral) SQL() string { return buildSQL(NullLiteral(0)) } 746 func (NullLiteral) addSQL(sb *strings.Builder) { sb.WriteString("NULL") } 747 748 func (StarExpr) SQL() string { return buildSQL(StarExpr(0)) } 749 func (StarExpr) addSQL(sb *strings.Builder) { sb.WriteString("*") } 750 751 func (il IntegerLiteral) SQL() string { return buildSQL(il) } 752 func (il IntegerLiteral) addSQL(sb *strings.Builder) { fmt.Fprintf(sb, "%d", il) } 753 754 func (fl FloatLiteral) SQL() string { return buildSQL(fl) } 755 func (fl FloatLiteral) addSQL(sb *strings.Builder) { fmt.Fprintf(sb, "%g", fl) } 756 757 // TODO: provide correct string quote method and use it. 758 759 func (sl StringLiteral) SQL() string { return buildSQL(sl) } 760 func (sl StringLiteral) addSQL(sb *strings.Builder) { fmt.Fprintf(sb, "%q", sl) } 761 762 func (bl BytesLiteral) SQL() string { return buildSQL(bl) } 763 func (bl BytesLiteral) addSQL(sb *strings.Builder) { fmt.Fprintf(sb, "B%q", bl) } 764 765 func (dl DateLiteral) SQL() string { return buildSQL(dl) } 766 func (dl DateLiteral) addSQL(sb *strings.Builder) { 767 fmt.Fprintf(sb, "DATE '%04d-%02d-%02d'", dl.Year, dl.Month, dl.Day) 768 } 769 770 func (tl TimestampLiteral) SQL() string { return buildSQL(tl) } 771 func (tl TimestampLiteral) addSQL(sb *strings.Builder) { 772 fmt.Fprintf(sb, "TIMESTAMP '%s'", time.Time(tl).Format("2006-01-02 15:04:05.000000-07:00")) 773 } 774 775 func (jl JSONLiteral) SQL() string { return buildSQL(jl) } 776 func (jl JSONLiteral) addSQL(sb *strings.Builder) { 777 fmt.Fprintf(sb, "JSON '%s'", jl) 778 }