Tokenize converts raw SQL bytes into a slice of tokens with position information. This is the main entry point for tokenization. It performs zero-copy tokenization directly on the input byte slice and returns tokens with precise start/end positions. Performance: 8M+ tokens/sec sustained throughput
(input []byte)
| 420 | // tokens, _ := tkz.Tokenize([]byte(sql)) |
| 421 | // // Correctly tokenizes Unicode identifiers and string literals |
| 422 | func (t *Tokenizer) Tokenize(input []byte) ([]models.TokenWithSpan, error) { |
| 423 | // Record start time for metrics |
| 424 | startTime := time.Now() |
| 425 | |
| 426 | // Validate input size to prevent DoS attacks |
| 427 | if len(input) > MaxInputSize { |
| 428 | err := errors.InputTooLargeError(int64(len(input)), MaxInputSize, models.Location{Line: 1, Column: 0}) |
| 429 | metrics.RecordTokenization(time.Since(startTime), len(input), err) |
| 430 | return nil, err |
| 431 | } |
| 432 | |
| 433 | // Reset state |
| 434 | t.Reset() |
| 435 | t.input = input |
| 436 | |
| 437 | // Pre-allocate line starts slice - reuse if possible |
| 438 | estimatedLines := len(input)/50 + 1 // Estimate 50 chars per line + 1 for initial 0 |
| 439 | if cap(t.lineStarts) < estimatedLines { |
| 440 | t.lineStarts = make([]int, 0, estimatedLines) |
| 441 | } else { |
| 442 | t.lineStarts = t.lineStarts[:0] |
| 443 | } |
| 444 | t.lineStarts = append(t.lineStarts, 0) |
| 445 | |
| 446 | // Pre-scan input to build line start indices |
| 447 | for i := 0; i < len(t.input); i++ { |
| 448 | if t.input[i] == '\n' { |
| 449 | t.lineStarts = append(t.lineStarts, i+1) |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | // Pre-allocate token slice with better capacity estimation |
| 454 | // More accurate estimation based on typical SQL token density |
| 455 | estimatedTokens := len(input) / 4 |
| 456 | if estimatedTokens < 16 { |
| 457 | estimatedTokens = 16 // At least 16 tokens |
| 458 | } |
| 459 | tokens := make([]models.TokenWithSpan, 0, estimatedTokens) |
| 460 | |
| 461 | // Get a buffer from the pool for string operations |
| 462 | buf := getBuffer() |
| 463 | defer putBuffer(buf) |
| 464 | |
| 465 | var tokenErr error |
| 466 | func() { |
| 467 | // Ensure proper cleanup even if we panic |
| 468 | defer func() { |
| 469 | if r := recover(); r != nil { |
| 470 | tokenErr = errors.TokenizerPanicError(r, t.getCurrentPosition()) |
| 471 | } |
| 472 | }() |
| 473 | |
| 474 | for t.pos.Index < len(t.input) { |
| 475 | t.skipWhitespace() |
| 476 | |
| 477 | if t.pos.Index >= len(t.input) { |
| 478 | break |
| 479 | } |