github.com/newrelic/go-agent@v3.26.0+incompatible/internal/sqlparse/sqlparse_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package sqlparse 5 6 import ( 7 "testing" 8 9 newrelic "github.com/newrelic/go-agent" 10 "github.com/newrelic/go-agent/internal/crossagent" 11 ) 12 13 type sqlTestcase struct { 14 Input string `json:"input"` 15 Operation string `json:"operation"` 16 Table string `json:"table"` 17 } 18 19 func (tc sqlTestcase) test(t *testing.T) { 20 var segment newrelic.DatastoreSegment 21 ParseQuery(&segment, tc.Input) 22 if tc.Operation == "other" { 23 // Allow for matching of Operation "other" to "" 24 if segment.Operation != "" { 25 t.Errorf("operation mismatch query='%s' wanted='%s' got='%s'", 26 tc.Input, tc.Operation, segment.Operation) 27 } 28 } else if segment.Operation != tc.Operation { 29 t.Errorf("operation mismatch query='%s' wanted='%s' got='%s'", 30 tc.Input, tc.Operation, segment.Operation) 31 } 32 // The Go agent subquery behavior does not match the PHP Agent. 33 if tc.Table == "(subquery)" { 34 return 35 } 36 if tc.Table != segment.Collection { 37 t.Errorf("table mismatch query='%s' wanted='%s' got='%s'", 38 tc.Input, tc.Table, segment.Collection) 39 } 40 } 41 42 func TestParseSQLCrossAgent(t *testing.T) { 43 var tcs []sqlTestcase 44 err := crossagent.ReadJSON("sql_parsing.json", &tcs) 45 if err != nil { 46 t.Fatal(err) 47 } 48 49 for _, tc := range tcs { 50 tc.test(t) 51 } 52 } 53 54 func TestParseSQLSubQuery(t *testing.T) { 55 for _, tc := range []sqlTestcase{ 56 {Input: "SELECT * FROM (SELECT * FROM foobar)", Operation: "select", Table: "foobar"}, 57 {Input: "SELECT * FROM (SELECT * FROM foobar) WHERE x > y", Operation: "select", Table: "foobar"}, 58 {Input: "SELECT * FROM(SELECT * FROM foobar) WHERE x > y", Operation: "select", Table: "foobar"}, 59 } { 60 tc.test(t) 61 } 62 } 63 64 func TestParseSQLOther(t *testing.T) { 65 for _, tc := range []sqlTestcase{ 66 // Test that we handle table names enclosed in brackets. 67 {Input: "SELECT * FROM [foo]", Operation: "select", Table: "foo"}, 68 {Input: "SELECT * FROM[foo]", Operation: "select", Table: "foo"}, 69 {Input: "SELECT * FROM [ foo ]", Operation: "select", Table: "foo"}, 70 {Input: "SELECT * FROM [ 'foo' ]", Operation: "select", Table: "foo"}, 71 {Input: "SELECT * FROM[ `something`.'foo' ]", Operation: "select", Table: "foo"}, 72 // Test that we handle the cheese. 73 {Input: "SELECT fromage FROM fromagier", Operation: "select", Table: "fromagier"}, 74 } { 75 tc.test(t) 76 } 77 } 78 79 func TestParseSQLUpdateExtraKeywords(t *testing.T) { 80 for _, tc := range []sqlTestcase{ 81 {Input: "update or rollback foo", Operation: "update", Table: "foo"}, 82 {Input: "update only foo", Operation: "update", Table: "foo"}, 83 {Input: "update low_priority ignore{foo}", Operation: "update", Table: "foo"}, 84 } { 85 tc.test(t) 86 } 87 } 88 89 func TestLineComment(t *testing.T) { 90 for _, tc := range []sqlTestcase{ 91 { 92 Input: `SELECT -- * FROM tricky 93 * FROM foo`, 94 Operation: "select", 95 Table: "foo", 96 }, 97 { 98 Input: `SELECT # * FROM tricky 99 * FROM foo`, 100 Operation: "select", 101 Table: "foo", 102 }, 103 { 104 Input: ` -- SELECT * FROM tricky 105 SELECT * FROM foo`, 106 Operation: "select", 107 Table: "foo", 108 }, 109 { 110 Input: ` # SELECT * FROM tricky 111 SELECT * FROM foo`, 112 Operation: "select", 113 Table: "foo", 114 }, 115 { 116 Input: `SELECT * FROM -- tricky 117 foo`, 118 Operation: "select", 119 Table: "foo", 120 }, 121 } { 122 tc.test(t) 123 } 124 } 125 126 func TestSemicolonPrefix(t *testing.T) { 127 for _, tc := range []sqlTestcase{ 128 { 129 Input: `;select * from foo`, 130 Operation: "select", 131 Table: "foo", 132 }, 133 { 134 Input: ` ;; ; select * from foo`, 135 Operation: "select", 136 Table: "foo", 137 }, 138 { 139 Input: ` ; 140 SELECT * FROM foo`, 141 Operation: "select", 142 Table: "foo", 143 }, 144 } { 145 tc.test(t) 146 } 147 } 148 149 func TestDollarSignTable(t *testing.T) { 150 for _, tc := range []sqlTestcase{ 151 { 152 Input: `select * from $dollar_100_$`, 153 Operation: "select", 154 Table: "$dollar_100_$", 155 }, 156 } { 157 tc.test(t) 158 } 159 } 160 161 func TestPriorityQuery(t *testing.T) { 162 // Test that we handle: 163 // https://dev.mysql.com/doc/refman/8.0/en/insert.html 164 // INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name 165 for _, tc := range []sqlTestcase{ 166 { 167 Input: `INSERT HIGH_PRIORITY INTO employee VALUES('Tom',12345,'Sales',100)`, 168 Operation: "insert", 169 Table: "employee", 170 }, 171 } { 172 tc.test(t) 173 } 174 } 175 176 func TestExtractTable(t *testing.T) { 177 for idx, tc := range []string{ 178 "table", 179 "`table`", 180 `"table"`, 181 "`database.table`", 182 "`database`.table", 183 "database.`table`", 184 "`database`.`table`", 185 " { table }", 186 "\n[table]", 187 "\t ( 'database'.`table` ) ", 188 } { 189 table := extractTable(tc) 190 if table != "table" { 191 t.Error(idx, table) 192 } 193 } 194 }