-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add mysql driver support for Sqlmetrics #14
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
## Sqlmetrics Unit tests | ||
```bash | ||
export LAST9_PQSQL_DSN="postgres://testuser:testpassword@localhost:5432/last9" | ||
export LAST9_MYSQL_DSN="mysql:password@tcp(localhost:8306)/last9" | ||
go test | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,8 +32,8 @@ var ( | |
connMap sync.Map | ||
) | ||
|
||
func storeConnInfo(conn *proxy.Conn, dsn string) error { | ||
info, err := parseDSN(dsn) | ||
func storeConnInfo(conn *proxy.Conn, driverName, dsn string) error { | ||
info, err := parseDSN(driverName, dsn) | ||
if err != nil { | ||
return errors.Wrap(err, "parse dsn") | ||
} | ||
|
@@ -51,8 +51,29 @@ func loadConnInfo(conn *proxy.Conn) *connInfo { | |
return info.(*connInfo) | ||
} | ||
|
||
func parseDSN(dsn string) (*connInfo, error) { | ||
u, err := dburl.Parse(dsn) | ||
// Note: the DB Url library expects a scheme in the DSN for | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not a correct way to handle this. Sql package supports magnitudes of queriersz we cannot switch case for all. Let the expected dan contain the scheme. |
||
// all drivers, while the standard library `database/sql` expects some | ||
// DSNs to be passed without a scheme (e.g. mysql) and some with | ||
// a scheme (e.g. postgres). | ||
// This function hence expects you to pass a driver name along with the DSN for | ||
// successfully parsing the DSN. | ||
func parseDSN(driver, dsn string) (*connInfo, error) { | ||
|
||
var SchemefullDSN string | ||
|
||
switch strings.Split(driver, ":")[0] { | ||
case "postgres": | ||
{ | ||
SchemefullDSN = dsn | ||
} | ||
case "mysql": | ||
{ | ||
//dsn = scheme + username + ":" + password + "@" + host + ":" + port + "/" + dbname | ||
SchemefullDSN = strings.Split(driver, ":")[0] + "://" + strings.Split(dsn, "@")[0] + "@" + strings.Split(strings.Split(dsn, "(")[1], ")")[0] + strings.Split(strings.Split(dsn, "(")[1], ")")[1] | ||
} | ||
} | ||
|
||
u, err := dburl.Parse(SchemefullDSN) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "invalid dsn") | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,15 @@ | ||
package sqlmetrics | ||
|
||
import ( | ||
"encoding/json" | ||
"log" | ||
"strings" | ||
|
||
"github.com/blastrain/vitess-sqlparser/sqlparser" | ||
pg_query "github.com/pganalyze/pg_query_go" | ||
"github.com/tidwall/gjson" | ||
) | ||
|
||
type LabelSet map[string]string | ||
|
||
// Merge accepts a set of LabelSets and merge them into this LabelSet. | ||
|
@@ -22,7 +32,7 @@ func (l LabelSet) ToMap() map[string]string { | |
|
||
// A type that user can extend to Parse a query and extract less verbose | ||
// or more relevant labels out of it. | ||
type LabelMaker func(string) LabelSet | ||
type LabelMaker func(string, string) LabelSet | ||
|
||
const idealLabelLen = 20 | ||
|
||
|
@@ -31,12 +41,14 @@ const idealLabelLen = 20 | |
// the metric server to its knees if left untapped. If this behaviour is not | ||
// desired, a user can anwyay implement their own QToLabelSet and emit the | ||
// raw query as-it-is. | ||
func defaultLabelMaker(q string) LabelSet { | ||
func defaultLabelMaker(driver, q string) LabelSet { | ||
statementName := getStatementName(driver, q) | ||
|
||
if len(q) > idealLabelLen { | ||
q = q[:idealLabelLen] + "..." | ||
} | ||
|
||
return LabelSet{"per": q} | ||
return LabelSet{"per": q, "statement": statementName} | ||
} | ||
|
||
// queryStatus is an enumeration. | ||
|
@@ -52,3 +64,48 @@ const ( | |
func (q queryStatus) String() string { | ||
return [...]string{"success", "failure"}[q] | ||
} | ||
|
||
// Accepts a driver name and query and returns the statement name of the first level query. | ||
func getStatementName(driver, q string) string { | ||
|
||
switch strings.Split(driver, ":")[0] { | ||
case "postgres": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Promote these strings as module global constants. Strings don't auto complete, variables do. |
||
{ | ||
// Parse query and get query AST | ||
tree, err := pg_query.ParseToJSON(q) | ||
if err != nil { | ||
log.Println("Error parsing query:", q, "Error occured :", err) | ||
return "" | ||
} | ||
|
||
// Get the statement name of the first level query | ||
statement := gjson.Get(tree[1:len(tree)-1], "[email protected]") | ||
|
||
return statement.String() | ||
} | ||
case "mysql": | ||
{ | ||
// Parse query and get query AST | ||
stmt, err := sqlparser.Parse(q) | ||
if err != nil { | ||
log.Println("Error parsing query:", q, "Error :", err) | ||
return "" | ||
} | ||
|
||
// convert AST to json string | ||
stmtJson, err := json.Marshal(stmt) | ||
if err != nil { | ||
log.Println("Error in conversion to json :", q, "Error :", err) | ||
return "" | ||
} | ||
|
||
// Parse the json and get the statement name of the first level query | ||
if gjson.Get(string(stmtJson), "SelectExprs").String() != "" { | ||
return "select" | ||
} | ||
return gjson.Get(string(stmtJson), "Action").String() | ||
} | ||
} | ||
|
||
return "" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use dburl package to derive this. Every dsn that connects already has this information. If it doesn't. It may not connect eirher.