/
sql.go
99 lines (84 loc) · 2.65 KB
/
sql.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/* Copyright 2019 Vox Media, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
package model
import (
"bufio"
"regexp"
"sort"
"strings"
)
var (
cCommentRe = regexp.MustCompile(`/\*[^*]*\*+(?:[^*/][^*]*\*+)*/`)
wholeLineCommentRe = regexp.MustCompile(`^\s*(--|#)`)
trailingCommentRe = regexp.MustCompile(`--|#`)
tokenRe = regexp.MustCompile(`[\s)(;,]+`)
)
func removeSQLComments(stmt string) string {
stmt = cCommentRe.ReplaceAllLiteralString(stmt, "")
var lines []string
scanner := bufio.NewScanner(strings.NewReader(stmt))
for scanner.Scan() {
line := scanner.Text()
if !wholeLineCommentRe.MatchString(line) {
lines = append(lines, trailingCommentRe.Split(line, -1)[0])
}
}
return strings.Join(lines, " ")
}
// Given an SQL query, return a (more or less accurate) list of
// every table name mentioned in it.
//
// It is okay for it to be inaccurate, we only need a function that
// errs by providing us false-positives. We can ignore all the tables
// whose names are not in our database. It cannot tell what
// table_date_range() and similar dynamic table selection functions
// will return, only BigQuery knows it.
func tablesInQuery(query string) []string {
query = removeSQLComments(query)
set := make(map[string]bool)
getNext := false
for _, tok := range tokenRe.Split(query, -1) {
ltok := strings.ToLower(tok)
if getNext {
if ltok != "" && ltok != "select" && ltok != "table_date_range" {
set[tok] = true
}
getNext = false
}
getNext = ltok == "from" || ltok == "join" || ltok == "table_date_range"
}
result := make([]string, 0, len(set))
for name, _ := range set {
result = append(result, name)
}
sort.Strings(result)
return result
}
// type PrimitiveSelect struct {
// Select string
// From string
// Where string
// Limit string
// }
// func (s *PrimitiveSelect) String() string {
// cols := s.Select
// if cols == "" {
// cols = "*"
// }
// stmt := fmt.Sprintf("SELECT %s \n FROM %s\n", cols, s.From)
// if s.Where != "" {
// stmt = fmt.Sprintf("%s WHERE %s\n", stmt, s.Where)
// }
// if s.Limit != "" {
// stmt = fmt.Sprintf("%s LIMIT %s", stmt, s.Limit)
// }
// return stmt
// }