applyMultiStatements splits the statement by semicolons and invokes f for each sub-statement with the text slice from the original statement and its [start, end) byte offsets. The text for each statement includes any leading whitespace from the end of the previous statement, matching the original be
(statement string, f func(text string, start, end int) error)
| 82 | // whitespace from the end of the previous statement, matching the original |
| 83 | // behavior needed for position tracking. |
| 84 | func applyMultiStatements(statement string, f func(text string, start, end int) error) error { |
| 85 | reader := bufio.NewReader(strings.NewReader(statement)) |
| 86 | delimiter := false |
| 87 | comment := false |
| 88 | done := false |
| 89 | hasContent := false |
| 90 | // byteOffset tracks our read position in the original statement. |
| 91 | byteOffset := 0 |
| 92 | // prevEnd tracks where the last emitted statement ended; the next |
| 93 | // statement's text will start here (to include inter-statement whitespace). |
| 94 | prevEnd := 0 |
| 95 | // contentEnd is the byte offset just past the last content character of |
| 96 | // the statement being accumulated (excludes trailing whitespace/newlines). |
| 97 | contentEnd := 0 |
| 98 | for !done { |
| 99 | line, err := reader.ReadString('\n') |
| 100 | if err != nil { |
| 101 | if err != io.EOF { |
| 102 | return err |
| 103 | } |
| 104 | done = true |
| 105 | } |
| 106 | lineLen := len(line) |
| 107 | line = strings.TrimRightFunc(line, unicode.IsSpace) |
| 108 | |
| 109 | execute := false |
| 110 | switch { |
| 111 | case strings.HasPrefix(line, "/*"): |
| 112 | if strings.Contains(line, "*/") { |
| 113 | if !strings.HasSuffix(line, "*/") { |
| 114 | return errors.Errorf("`*/` must be the end of the line; new statement should start as a new line") |
| 115 | } |
| 116 | } else { |
| 117 | comment = true |
| 118 | } |
| 119 | case comment && !strings.Contains(line, "*/"): |
| 120 | // skip line in comment mode |
| 121 | case comment && strings.Contains(line, "*/"): |
| 122 | if !strings.HasSuffix(line, "*/") { |
| 123 | return errors.Errorf("`*/` must be the end of the line; new statement should start as a new line") |
| 124 | } |
| 125 | comment = false |
| 126 | case !hasContent && line == "": |
| 127 | // skip leading blank lines |
| 128 | case strings.HasPrefix(line, "--"): |
| 129 | // skip comment lines |
| 130 | case line == "DELIMITER ;;": |
| 131 | delimiter = true |
| 132 | case line == "DELIMITER ;" && delimiter: |
| 133 | delimiter = false |
| 134 | execute = true |
| 135 | case strings.HasSuffix(line, ";"): |
| 136 | hasContent = true |
| 137 | contentEnd = byteOffset + len(line) |
| 138 | if !delimiter { |
| 139 | execute = true |
| 140 | } |
| 141 | default: |