From 853a95e0b365f21191baf90955fd01b1886032df Mon Sep 17 00:00:00 2001
From: Edd Robinson <me@edd.io>
Date: Tue, 24 May 2016 12:25:01 +0100
Subject: [PATCH] Reduce allocations on Query's io.Stringer implementation

A query's String method is called multiple times per query. This commit
ensures all calls to query.String share use of a strings.NewReplacer.

This approximately halves the number of allocations for the benchmarked
query.
---
 CHANGELOG.md         |  1 +
 influxql/ast_test.go |  8 ++++++++
 influxql/parser.go   | 11 +++++++----
 3 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0ffc55cd13..fc05849749 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
 - [#6655](https://github.com/influxdata/influxdb/issues/6655): Add HTTP(s) based subscriptions.
 - [#5906](https://github.com/influxdata/influxdb/issues/5906): Dynamically update the documentation link in the admin UI.
 - [#6686](https://github.com/influxdata/influxdb/pull/6686): Optimize timestamp run-length decoding
+- [#6713](https://github.com/influxdata/influxdb/pull/6713): Reduce allocations during query parsing.
 
 ### Bugfixes
 
diff --git a/influxql/ast_test.go b/influxql/ast_test.go
index 34cc07f8f3..7a95fbabcf 100644
--- a/influxql/ast_test.go
+++ b/influxql/ast_test.go
@@ -10,6 +10,14 @@ import (
 	"github.com/influxdata/influxdb/influxql"
 )
 
+func BenchmarkQuery_String(b *testing.B) {
+	p := influxql.NewParser(strings.NewReader(`SELECT foo AS zoo, a AS b FROM bar WHERE value > 10 AND q = 'hello'`))
+	q, _ := p.ParseStatement()
+	for i := 0; i < b.N; i++ {
+		_ = q.String()
+	}
+}
+
 // Ensure a value's data type can be retrieved.
 func TestInspectDataType(t *testing.T) {
 	for i, tt := range []struct {
diff --git a/influxql/parser.go b/influxql/parser.go
index e8272bb928..7c4c28fd22 100644
--- a/influxql/parser.go
+++ b/influxql/parser.go
@@ -2680,15 +2680,18 @@ func (p *Parser) parseTokenMaybe(expected Token) bool {
 	return true
 }
 
+var (
+	qsReplacer = strings.NewReplacer("\n", `\n`, `\`, `\\`, `'`, `\'`)
+	qiReplacer = strings.NewReplacer("\n", `\n`, `\`, `\\`, `"`, `\"`)
+)
+
 // QuoteString returns a quoted string.
 func QuoteString(s string) string {
-	return `'` + strings.NewReplacer("\n", `\n`, `\`, `\\`, `'`, `\'`).Replace(s) + `'`
+	return `'` + qsReplacer.Replace(s) + `'`
 }
 
 // QuoteIdent returns a quoted identifier from multiple bare identifiers.
 func QuoteIdent(segments ...string) string {
-	r := strings.NewReplacer("\n", `\n`, `\`, `\\`, `"`, `\"`)
-
 	var buf bytes.Buffer
 	for i, segment := range segments {
 		needQuote := IdentNeedsQuotes(segment) ||
@@ -2698,7 +2701,7 @@ func QuoteIdent(segments ...string) string {
 			_ = buf.WriteByte('"')
 		}
 
-		_, _ = buf.WriteString(r.Replace(segment))
+		_, _ = buf.WriteString(qiReplacer.Replace(segment))
 
 		if needQuote {
 			_ = buf.WriteByte('"')