github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/sqlparse/tidbparser/ast/misc.go (about) 1 // Copyright 2015 PingCAP, 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package ast 15 16 import ( 17 "bytes" 18 "fmt" 19 "strings" 20 21 "github.com/bingoohuang/gg/pkg/sqlparse/tidbparser/dependency/model" 22 "github.com/bingoohuang/gg/pkg/sqlparse/tidbparser/dependency/mysql" 23 "github.com/bingoohuang/gg/pkg/sqlparse/tidbparser/dependency/util/auth" 24 ) 25 26 var ( 27 _ StmtNode = &AdminStmt{} 28 _ StmtNode = &AlterUserStmt{} 29 _ StmtNode = &BeginStmt{} 30 _ StmtNode = &BinlogStmt{} 31 _ StmtNode = &CommitStmt{} 32 _ StmtNode = &CreateUserStmt{} 33 _ StmtNode = &DeallocateStmt{} 34 _ StmtNode = &DoStmt{} 35 _ StmtNode = &ExecuteStmt{} 36 _ StmtNode = &ExplainStmt{} 37 _ StmtNode = &GrantStmt{} 38 _ StmtNode = &PrepareStmt{} 39 _ StmtNode = &RollbackStmt{} 40 _ StmtNode = &SetPwdStmt{} 41 _ StmtNode = &SetStmt{} 42 _ StmtNode = &UseStmt{} 43 _ StmtNode = &FlushStmt{} 44 _ StmtNode = &KillStmt{} 45 46 _ Node = &PrivElem{} 47 _ Node = &VariableAssignment{} 48 ) 49 50 // Isolation level constants. 51 const ( 52 ReadCommitted = "READ-COMMITTED" 53 ReadUncommitted = "READ-UNCOMMITTED" 54 Serializable = "SERIALIZABLE" 55 RepeatableRead = "REPEATABLE-READ" 56 57 // Valid formats for explain statement. 58 ExplainFormatROW = "row" 59 ExplainFormatDOT = "dot" 60 ) 61 62 // ExplainFormats stores the valid formats for explain statement, used by validator. 63 var ExplainFormats = []string{ 64 ExplainFormatROW, 65 ExplainFormatDOT, 66 } 67 68 // TypeOpt is used for parsing data type option from SQL. 69 type TypeOpt struct { 70 IsUnsigned bool 71 IsZerofill bool 72 } 73 74 // FloatOpt is used for parsing floating-point type option from SQL. 75 // See http://dev.mysql.com/doc/refman/5.7/en/floating-point-types.html 76 type FloatOpt struct { 77 Flen int 78 Decimal int 79 } 80 81 // AuthOption is used for parsing create use statement. 82 type AuthOption struct { 83 // ByAuthString set as true, if AuthString is used for authorization. Otherwise, authorization is done by HashString. 84 ByAuthString bool 85 AuthString string 86 HashString string 87 // TODO: support auth_plugin 88 } 89 90 // ExplainStmt is a statement to provide information about how is SQL statement executed 91 // or get columns information in a table. 92 // See https://dev.mysql.com/doc/refman/5.7/en/explain.html 93 type ExplainStmt struct { 94 stmtNode 95 96 Stmt StmtNode 97 Format string 98 } 99 100 // Accept implements Node Accept interface. 101 func (n *ExplainStmt) Accept(v Visitor) (Node, bool) { 102 newNode, skipChildren := v.Enter(n) 103 if skipChildren { 104 return v.Leave(newNode) 105 } 106 n = newNode.(*ExplainStmt) 107 node, ok := n.Stmt.Accept(v) 108 if !ok { 109 return n, false 110 } 111 n.Stmt = node.(DMLNode) 112 return v.Leave(n) 113 } 114 115 // PrepareStmt is a statement to prepares a SQL statement which contains placeholders, 116 // and it is executed with ExecuteStmt and released with DeallocateStmt. 117 // See https://dev.mysql.com/doc/refman/5.7/en/prepare.html 118 type PrepareStmt struct { 119 stmtNode 120 121 Name string 122 SQLText string 123 SQLVar *VariableExpr 124 } 125 126 // Accept implements Node Accept interface. 127 func (n *PrepareStmt) Accept(v Visitor) (Node, bool) { 128 newNode, skipChildren := v.Enter(n) 129 if skipChildren { 130 return v.Leave(newNode) 131 } 132 n = newNode.(*PrepareStmt) 133 if n.SQLVar != nil { 134 node, ok := n.SQLVar.Accept(v) 135 if !ok { 136 return n, false 137 } 138 n.SQLVar = node.(*VariableExpr) 139 } 140 return v.Leave(n) 141 } 142 143 // DeallocateStmt is a statement to release PreparedStmt. 144 // See https://dev.mysql.com/doc/refman/5.7/en/deallocate-prepare.html 145 type DeallocateStmt struct { 146 stmtNode 147 148 Name string 149 } 150 151 // Accept implements Node Accept interface. 152 func (n *DeallocateStmt) Accept(v Visitor) (Node, bool) { 153 newNode, skipChildren := v.Enter(n) 154 if skipChildren { 155 return v.Leave(newNode) 156 } 157 n = newNode.(*DeallocateStmt) 158 return v.Leave(n) 159 } 160 161 // ExecuteStmt is a statement to execute PreparedStmt. 162 // See https://dev.mysql.com/doc/refman/5.7/en/execute.html 163 type ExecuteStmt struct { 164 stmtNode 165 166 Name string 167 UsingVars []ExprNode 168 ExecID uint32 169 } 170 171 // Accept implements Node Accept interface. 172 func (n *ExecuteStmt) Accept(v Visitor) (Node, bool) { 173 newNode, skipChildren := v.Enter(n) 174 if skipChildren { 175 return v.Leave(newNode) 176 } 177 n = newNode.(*ExecuteStmt) 178 for i, val := range n.UsingVars { 179 node, ok := val.Accept(v) 180 if !ok { 181 return n, false 182 } 183 n.UsingVars[i] = node.(ExprNode) 184 } 185 return v.Leave(n) 186 } 187 188 // BeginStmt is a statement to start a new transaction. 189 // See https://dev.mysql.com/doc/refman/5.7/en/commit.html 190 type BeginStmt struct { 191 stmtNode 192 } 193 194 // Accept implements Node Accept interface. 195 func (n *BeginStmt) Accept(v Visitor) (Node, bool) { 196 newNode, skipChildren := v.Enter(n) 197 if skipChildren { 198 return v.Leave(newNode) 199 } 200 n = newNode.(*BeginStmt) 201 return v.Leave(n) 202 } 203 204 // BinlogStmt is an internal-use statement. 205 // We just parse and ignore it. 206 // See http://dev.mysql.com/doc/refman/5.7/en/binlog.html 207 type BinlogStmt struct { 208 stmtNode 209 Str string 210 } 211 212 // Accept implements Node Accept interface. 213 func (n *BinlogStmt) Accept(v Visitor) (Node, bool) { 214 newNode, skipChildren := v.Enter(n) 215 if skipChildren { 216 return v.Leave(newNode) 217 } 218 n = newNode.(*BinlogStmt) 219 return v.Leave(n) 220 } 221 222 // CommitStmt is a statement to commit the current transaction. 223 // See https://dev.mysql.com/doc/refman/5.7/en/commit.html 224 type CommitStmt struct { 225 stmtNode 226 } 227 228 // Accept implements Node Accept interface. 229 func (n *CommitStmt) Accept(v Visitor) (Node, bool) { 230 newNode, skipChildren := v.Enter(n) 231 if skipChildren { 232 return v.Leave(newNode) 233 } 234 n = newNode.(*CommitStmt) 235 return v.Leave(n) 236 } 237 238 // RollbackStmt is a statement to roll back the current transaction. 239 // See https://dev.mysql.com/doc/refman/5.7/en/commit.html 240 type RollbackStmt struct { 241 stmtNode 242 } 243 244 // Accept implements Node Accept interface. 245 func (n *RollbackStmt) Accept(v Visitor) (Node, bool) { 246 newNode, skipChildren := v.Enter(n) 247 if skipChildren { 248 return v.Leave(newNode) 249 } 250 n = newNode.(*RollbackStmt) 251 return v.Leave(n) 252 } 253 254 // UseStmt is a statement to use the DBName database as the current database. 255 // See https://dev.mysql.com/doc/refman/5.7/en/use.html 256 type UseStmt struct { 257 stmtNode 258 259 DBName string 260 } 261 262 // Accept implements Node Accept interface. 263 func (n *UseStmt) Accept(v Visitor) (Node, bool) { 264 newNode, skipChildren := v.Enter(n) 265 if skipChildren { 266 return v.Leave(newNode) 267 } 268 n = newNode.(*UseStmt) 269 return v.Leave(n) 270 } 271 272 const ( 273 // SetNames is the const for set names/charset stmt. 274 // If VariableAssignment.Name == Names, it should be set names/charset stmt. 275 SetNames = "SetNAMES" 276 ) 277 278 // VariableAssignment is a variable assignment struct. 279 type VariableAssignment struct { 280 node 281 Name string 282 Value ExprNode 283 IsGlobal bool 284 IsSystem bool 285 286 // ExtendValue is a way to store extended info. 287 // VariableAssignment should be able to store information for SetCharset/SetPWD Stmt. 288 // For SetCharsetStmt, Value is charset, ExtendValue is collation. 289 // TODO: Use SetStmt to implement set password statement. 290 ExtendValue *ValueExpr 291 } 292 293 // Accept implements Node interface. 294 func (n *VariableAssignment) Accept(v Visitor) (Node, bool) { 295 newNode, skipChildren := v.Enter(n) 296 if skipChildren { 297 return v.Leave(newNode) 298 } 299 n = newNode.(*VariableAssignment) 300 node, ok := n.Value.Accept(v) 301 if !ok { 302 return n, false 303 } 304 n.Value = node.(ExprNode) 305 return v.Leave(n) 306 } 307 308 // FlushStmtType is the type for FLUSH statement. 309 type FlushStmtType int 310 311 // Flush statement types. 312 const ( 313 FlushNone FlushStmtType = iota 314 FlushTables 315 FlushPrivileges 316 ) 317 318 // FlushStmt is a statement to flush tables/privileges/optimizer costs and so on. 319 type FlushStmt struct { 320 stmtNode 321 322 Tp FlushStmtType // Privileges/Tables/... 323 NoWriteToBinLog bool 324 Tables []*TableName // For FlushTableStmt, if Tables is empty, it means flush all tables. 325 ReadLock bool 326 } 327 328 // Accept implements Node Accept interface. 329 func (n *FlushStmt) Accept(v Visitor) (Node, bool) { 330 newNode, skipChildren := v.Enter(n) 331 if skipChildren { 332 return v.Leave(newNode) 333 } 334 n = newNode.(*FlushStmt) 335 return v.Leave(n) 336 } 337 338 // KillStmt is a statement to kill a query or connection. 339 type KillStmt struct { 340 stmtNode 341 342 // Query indicates whether terminate a single query on this connection or the whole connection. 343 // If Query is true, terminates the statement the connection is currently executing, but leaves the connection itself intact. 344 // If Query is false, terminates the connection associated with the given ConnectionID, after terminating any statement the connection is executing. 345 Query bool 346 ConnectionID uint64 347 // TiDBExtension is used to indicate whether the user knows he is sending kill statement to the right tidb-server. 348 // When the SQL grammar is "KILL TIDB [CONNECTION | QUERY] connectionID", TiDBExtension will be set. 349 // It's a special grammar extension in TiDB. This extension exists because, when the connection is: 350 // client -> LVS proxy -> TiDB, and type Ctrl+C in client, the following action will be executed: 351 // new a connection; kill xxx; 352 // kill command may send to the wrong TiDB, because the exists of LVS proxy, and kill the wrong session. 353 // So, "KILL TIDB" grammar is introduced, and it REQUIRES DIRECT client -> TiDB TOPOLOGY. 354 // TODO: The standard KILL grammar will be supported once we have global connectionID. 355 TiDBExtension bool 356 } 357 358 // Accept implements Node Accept interface. 359 func (n *KillStmt) Accept(v Visitor) (Node, bool) { 360 newNode, skipChildren := v.Enter(n) 361 if skipChildren { 362 return v.Leave(newNode) 363 } 364 n = newNode.(*KillStmt) 365 return v.Leave(n) 366 } 367 368 // SetStmt is the statement to set variables. 369 type SetStmt struct { 370 stmtNode 371 // Variables is the list of variable assignment. 372 Variables []*VariableAssignment 373 } 374 375 // Accept implements Node Accept interface. 376 func (n *SetStmt) Accept(v Visitor) (Node, bool) { 377 newNode, skipChildren := v.Enter(n) 378 if skipChildren { 379 return v.Leave(newNode) 380 } 381 n = newNode.(*SetStmt) 382 for i, val := range n.Variables { 383 node, ok := val.Accept(v) 384 if !ok { 385 return n, false 386 } 387 n.Variables[i] = node.(*VariableAssignment) 388 } 389 return v.Leave(n) 390 } 391 392 /* 393 // SetCharsetStmt is a statement to assign values to character and collation variables. 394 // See https://dev.mysql.com/doc/refman/5.7/en/set-statement.html 395 type SetCharsetStmt struct { 396 stmtNode 397 398 Charset string 399 Collate string 400 } 401 402 // Accept implements Node Accept interface. 403 func (n *SetCharsetStmt) Accept(v Visitor) (Node, bool) { 404 newNode, skipChildren := v.Enter(n) 405 if skipChildren { 406 return v.Leave(newNode) 407 } 408 n = newNode.(*SetCharsetStmt) 409 return v.Leave(n) 410 } 411 */ 412 413 // SetPwdStmt is a statement to assign a password to user account. 414 // See https://dev.mysql.com/doc/refman/5.7/en/set-password.html 415 type SetPwdStmt struct { 416 stmtNode 417 418 User *auth.UserIdentity 419 Password string 420 } 421 422 // SecureText implements SensitiveStatement interface. 423 func (n *SetPwdStmt) SecureText() string { 424 return fmt.Sprintf("set password for user %s", n.User) 425 } 426 427 // Accept implements Node Accept interface. 428 func (n *SetPwdStmt) Accept(v Visitor) (Node, bool) { 429 newNode, skipChildren := v.Enter(n) 430 if skipChildren { 431 return v.Leave(newNode) 432 } 433 n = newNode.(*SetPwdStmt) 434 return v.Leave(n) 435 } 436 437 // UserSpec is used for parsing create user statement. 438 type UserSpec struct { 439 User *auth.UserIdentity 440 AuthOpt *AuthOption 441 } 442 443 // SecurityString formats the UserSpec without password information. 444 func (u *UserSpec) SecurityString() string { 445 withPassword := false 446 if opt := u.AuthOpt; opt != nil { 447 if len(opt.AuthString) > 0 || len(opt.HashString) > 0 { 448 withPassword = true 449 } 450 } 451 if withPassword { 452 return fmt.Sprintf("{%s password = ***}", u.User) 453 } 454 return u.User.String() 455 } 456 457 // EncodedPassword returns the encoded password (which is the real data mysql.user). 458 // The boolean value indicates input's password format is legal or not. 459 func (u *UserSpec) EncodedPassword() (string, bool) { 460 if u.AuthOpt == nil { 461 return "", true 462 } 463 464 opt := u.AuthOpt 465 if opt.ByAuthString { 466 return auth.EncodePassword(opt.AuthString), true 467 } 468 469 // Not a legal password string. 470 if len(opt.HashString) != 41 || !strings.HasPrefix(opt.HashString, "*") { 471 return "", false 472 } 473 return opt.HashString, true 474 } 475 476 // CreateUserStmt creates user account. 477 // See https://dev.mysql.com/doc/refman/5.7/en/create-user.html 478 type CreateUserStmt struct { 479 stmtNode 480 481 IfNotExists bool 482 Specs []*UserSpec 483 } 484 485 // Accept implements Node Accept interface. 486 func (n *CreateUserStmt) Accept(v Visitor) (Node, bool) { 487 newNode, skipChildren := v.Enter(n) 488 if skipChildren { 489 return v.Leave(newNode) 490 } 491 n = newNode.(*CreateUserStmt) 492 return v.Leave(n) 493 } 494 495 // SecureText implements SensitiveStatement interface. 496 func (n *CreateUserStmt) SecureText() string { 497 var buf bytes.Buffer 498 buf.WriteString("create user") 499 for _, user := range n.Specs { 500 buf.WriteString(" ") 501 buf.WriteString(user.SecurityString()) 502 } 503 return buf.String() 504 } 505 506 // AlterUserStmt modifies user account. 507 // See https://dev.mysql.com/doc/refman/5.7/en/alter-user.html 508 type AlterUserStmt struct { 509 stmtNode 510 511 IfExists bool 512 CurrentAuth *AuthOption 513 Specs []*UserSpec 514 } 515 516 // SecureText implements SensitiveStatement interface. 517 func (n *AlterUserStmt) SecureText() string { 518 var buf bytes.Buffer 519 buf.WriteString("alter user") 520 for _, user := range n.Specs { 521 buf.WriteString(" ") 522 buf.WriteString(user.SecurityString()) 523 } 524 return buf.String() 525 } 526 527 // Accept implements Node Accept interface. 528 func (n *AlterUserStmt) Accept(v Visitor) (Node, bool) { 529 newNode, skipChildren := v.Enter(n) 530 if skipChildren { 531 return v.Leave(newNode) 532 } 533 n = newNode.(*AlterUserStmt) 534 return v.Leave(n) 535 } 536 537 // DropUserStmt creates user account. 538 // See http://dev.mysql.com/doc/refman/5.7/en/drop-user.html 539 type DropUserStmt struct { 540 stmtNode 541 542 IfExists bool 543 UserList []*auth.UserIdentity 544 } 545 546 // Accept implements Node Accept interface. 547 func (n *DropUserStmt) Accept(v Visitor) (Node, bool) { 548 newNode, skipChildren := v.Enter(n) 549 if skipChildren { 550 return v.Leave(newNode) 551 } 552 n = newNode.(*DropUserStmt) 553 return v.Leave(n) 554 } 555 556 // DoStmt is the struct for DO statement. 557 type DoStmt struct { 558 stmtNode 559 560 Exprs []ExprNode 561 } 562 563 // Accept implements Node Accept interface. 564 func (n *DoStmt) Accept(v Visitor) (Node, bool) { 565 newNode, skipChildren := v.Enter(n) 566 if skipChildren { 567 return v.Leave(newNode) 568 } 569 n = newNode.(*DoStmt) 570 for i, val := range n.Exprs { 571 node, ok := val.Accept(v) 572 if !ok { 573 return n, false 574 } 575 n.Exprs[i] = node.(ExprNode) 576 } 577 return v.Leave(n) 578 } 579 580 // AdminStmtType is the type for admin statement. 581 type AdminStmtType int 582 583 // Admin statement types. 584 const ( 585 AdminShowDDL = iota + 1 586 AdminCheckTable 587 AdminShowDDLJobs 588 AdminCancelDDLJobs 589 AdminCheckIndex 590 ) 591 592 // AdminStmt is the struct for Admin statement. 593 type AdminStmt struct { 594 stmtNode 595 596 Tp AdminStmtType 597 Index string 598 Tables []*TableName 599 JobIDs []int64 600 } 601 602 // Accept implements Node Accpet interface. 603 func (n *AdminStmt) Accept(v Visitor) (Node, bool) { 604 newNode, skipChildren := v.Enter(n) 605 if skipChildren { 606 return v.Leave(newNode) 607 } 608 609 n = newNode.(*AdminStmt) 610 for i, val := range n.Tables { 611 node, ok := val.Accept(v) 612 if !ok { 613 return n, false 614 } 615 n.Tables[i] = node.(*TableName) 616 } 617 618 return v.Leave(n) 619 } 620 621 // PrivElem is the privilege type and optional column list. 622 type PrivElem struct { 623 node 624 625 Priv mysql.PrivilegeType 626 Cols []*ColumnName 627 } 628 629 // Accept implements Node Accept interface. 630 func (n *PrivElem) Accept(v Visitor) (Node, bool) { 631 newNode, skipChildren := v.Enter(n) 632 if skipChildren { 633 return v.Leave(newNode) 634 } 635 n = newNode.(*PrivElem) 636 for i, val := range n.Cols { 637 node, ok := val.Accept(v) 638 if !ok { 639 return n, false 640 } 641 n.Cols[i] = node.(*ColumnName) 642 } 643 return v.Leave(n) 644 } 645 646 // ObjectTypeType is the type for object type. 647 type ObjectTypeType int 648 649 const ( 650 // ObjectTypeNone is for empty object type. 651 ObjectTypeNone ObjectTypeType = iota + 1 652 // ObjectTypeTable means the following object is a table. 653 ObjectTypeTable 654 ) 655 656 // GrantLevelType is the type for grant level. 657 type GrantLevelType int 658 659 const ( 660 // GrantLevelNone is the dummy const for default value. 661 GrantLevelNone GrantLevelType = iota + 1 662 // GrantLevelGlobal means the privileges are administrative or apply to all databases on a given server. 663 GrantLevelGlobal 664 // GrantLevelDB means the privileges apply to all objects in a given database. 665 GrantLevelDB 666 // GrantLevelTable means the privileges apply to all columns in a given table. 667 GrantLevelTable 668 ) 669 670 // GrantLevel is used for store the privilege scope. 671 type GrantLevel struct { 672 Level GrantLevelType 673 DBName string 674 TableName string 675 } 676 677 // RevokeStmt is the struct for REVOKE statement. 678 type RevokeStmt struct { 679 stmtNode 680 681 Privs []*PrivElem 682 ObjectType ObjectTypeType 683 Level *GrantLevel 684 Users []*UserSpec 685 } 686 687 // Accept implements Node Accept interface. 688 func (n *RevokeStmt) Accept(v Visitor) (Node, bool) { 689 newNode, skipChildren := v.Enter(n) 690 if skipChildren { 691 return v.Leave(newNode) 692 } 693 n = newNode.(*RevokeStmt) 694 for i, val := range n.Privs { 695 node, ok := val.Accept(v) 696 if !ok { 697 return n, false 698 } 699 n.Privs[i] = node.(*PrivElem) 700 } 701 return v.Leave(n) 702 } 703 704 // GrantStmt is the struct for GRANT statement. 705 type GrantStmt struct { 706 stmtNode 707 708 Privs []*PrivElem 709 ObjectType ObjectTypeType 710 Level *GrantLevel 711 Users []*UserSpec 712 WithGrant bool 713 } 714 715 // SecureText implements SensitiveStatement interface. 716 func (n *GrantStmt) SecureText() string { 717 text := n.text 718 // Filter "identified by xxx" because it would expose password information. 719 idx := strings.Index(strings.ToLower(text), "identified") 720 if idx > 0 { 721 text = text[:idx] 722 } 723 return text 724 } 725 726 // Accept implements Node Accept interface. 727 func (n *GrantStmt) Accept(v Visitor) (Node, bool) { 728 newNode, skipChildren := v.Enter(n) 729 if skipChildren { 730 return v.Leave(newNode) 731 } 732 n = newNode.(*GrantStmt) 733 for i, val := range n.Privs { 734 node, ok := val.Accept(v) 735 if !ok { 736 return n, false 737 } 738 n.Privs[i] = node.(*PrivElem) 739 } 740 return v.Leave(n) 741 } 742 743 // Ident is the table identifier composed of schema name and table name. 744 type Ident struct { 745 Schema model.CIStr 746 Name model.CIStr 747 } 748 749 // String implements fmt.Stringer interface. 750 func (i Ident) String() string { 751 if i.Schema.O == "" { 752 return i.Name.O 753 } 754 return fmt.Sprintf("%s.%s", i.Schema, i.Name) 755 } 756 757 // SelectStmtOpts wrap around select hints and switches 758 type SelectStmtOpts struct { 759 Distinct bool 760 SQLCache bool 761 CalcFoundRows bool 762 Priority mysql.PriorityEnum 763 TableHints []*TableOptimizerHint 764 } 765 766 // TableOptimizerHint is Table level optimizer hint 767 type TableOptimizerHint struct { 768 node 769 // HintName is the name or alias of the table(s) which the hint will affect. 770 // Table hints has no schema info 771 // It allows only table name or alias (if table has an alias) 772 HintName model.CIStr 773 Tables []model.CIStr 774 } 775 776 // Accept implements Node Accept interface. 777 func (n *TableOptimizerHint) Accept(v Visitor) (Node, bool) { 778 newNode, skipChildren := v.Enter(n) 779 if skipChildren { 780 return v.Leave(newNode) 781 } 782 n = newNode.(*TableOptimizerHint) 783 return v.Leave(n) 784 }