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  }