Skip to content

Commit 133ac67

Browse files
authored
Improved the performance of CopyIn and CopyInSchema and added BufferQuoteIdentifier (#1100)
1 parent d5affd5 commit 133ac67

File tree

3 files changed

+48
-20
lines changed

3 files changed

+48
-20
lines changed

conn.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package pq
22

33
import (
44
"bufio"
5+
"bytes"
56
"context"
67
"crypto/md5"
78
"crypto/sha256"
@@ -1631,10 +1632,10 @@ func (rs *rows) NextResultSet() error {
16311632
// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be
16321633
// used as part of an SQL statement. For example:
16331634
//
1634-
// tblname := "my_table"
1635-
// data := "my_data"
1636-
// quoted := pq.QuoteIdentifier(tblname)
1637-
// err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data)
1635+
//tblname := "my_table"
1636+
//data := "my_data"
1637+
//quoted := pq.QuoteIdentifier(tblname)
1638+
//err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data)
16381639
//
16391640
// Any double quotes in name will be escaped. The quoted identifier will be
16401641
// case sensitive when used in a query. If the input string contains a zero
@@ -1647,12 +1648,24 @@ func QuoteIdentifier(name string) string {
16471648
return `"` + strings.Replace(name, `"`, `""`, -1) + `"`
16481649
}
16491650

1651+
// BufferQuoteIdentifier satisfies the same purpose as QuoteIdentifier, but backed by a
1652+
// byte buffer.
1653+
func BufferQuoteIdentifier(name string, buffer *bytes.Buffer) {
1654+
end := strings.IndexRune(name, 0)
1655+
if end > -1 {
1656+
name = name[:end]
1657+
}
1658+
buffer.WriteRune('"')
1659+
buffer.WriteString(strings.Replace(name, `"`, `""`, -1))
1660+
buffer.WriteRune('"')
1661+
}
1662+
16501663
// QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal
16511664
// to DDL and other statements that do not accept parameters) to be used as part
16521665
// of an SQL statement. For example:
16531666
//
1654-
// exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z")
1655-
// err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date))
1667+
//exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z")
1668+
//err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date))
16561669
//
16571670
// Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be
16581671
// replaced by two backslashes (i.e. "\\") and the C-style escape identifier

copy.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package pq
22

33
import (
4+
"bytes"
45
"context"
56
"database/sql/driver"
67
"encoding/binary"
@@ -20,29 +21,35 @@ var (
2021
// CopyIn creates a COPY FROM statement which can be prepared with
2122
// Tx.Prepare(). The target table should be visible in search_path.
2223
func CopyIn(table string, columns ...string) string {
23-
stmt := "COPY " + QuoteIdentifier(table) + " ("
24+
buffer := bytes.NewBufferString("COPY ")
25+
BufferQuoteIdentifier(table, buffer)
26+
buffer.WriteString(" (")
27+
makeStmt(buffer, columns...)
28+
return buffer.String()
29+
}
30+
31+
// MakeStmt makes the stmt string for CopyIn and CopyInSchema.
32+
func makeStmt(buffer *bytes.Buffer, columns ...string) {
33+
//s := bytes.NewBufferString()
2434
for i, col := range columns {
2535
if i != 0 {
26-
stmt += ", "
36+
buffer.WriteString(", ")
2737
}
28-
stmt += QuoteIdentifier(col)
38+
BufferQuoteIdentifier(col, buffer)
2939
}
30-
stmt += ") FROM STDIN"
31-
return stmt
40+
buffer.WriteString(") FROM STDIN")
3241
}
3342

3443
// CopyInSchema creates a COPY FROM statement which can be prepared with
3544
// Tx.Prepare().
3645
func CopyInSchema(schema, table string, columns ...string) string {
37-
stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " ("
38-
for i, col := range columns {
39-
if i != 0 {
40-
stmt += ", "
41-
}
42-
stmt += QuoteIdentifier(col)
43-
}
44-
stmt += ") FROM STDIN"
45-
return stmt
46+
buffer := bytes.NewBufferString("COPY ")
47+
BufferQuoteIdentifier(schema, buffer)
48+
buffer.WriteRune('.')
49+
BufferQuoteIdentifier(table, buffer)
50+
buffer.WriteString(" (")
51+
makeStmt(buffer, columns...)
52+
return buffer.String()
4653
}
4754

4855
type copyin struct {

copy_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,3 +500,11 @@ func BenchmarkCopyIn(b *testing.B) {
500500
b.Fatalf("expected %d items, not %d", b.N, num)
501501
}
502502
}
503+
504+
var bigTableColumns = []string{"ABIOGENETICALLY", "ABORIGINALITIES", "ABSORBABILITIES", "ABSORBEFACIENTS", "ABSORPTIOMETERS", "ABSTRACTIONISMS", "ABSTRACTIONISTS", "ACANTHOCEPHALAN", "ACCEPTABILITIES", "ACCEPTINGNESSES", "ACCESSARINESSES", "ACCESSIBILITIES", "ACCESSORINESSES", "ACCIDENTALITIES", "ACCIDENTOLOGIES", "ACCLIMATISATION", "ACCLIMATIZATION", "ACCOMMODATINGLY", "ACCOMMODATIONAL", "ACCOMPLISHMENTS", "ACCOUNTABLENESS", "ACCOUNTANTSHIPS", "ACCULTURATIONAL", "ACETOPHENETIDIN", "ACETYLSALICYLIC", "ACHONDROPLASIAS", "ACHONDROPLASTIC", "ACHROMATICITIES", "ACHROMATISATION", "ACHROMATIZATION", "ACIDIMETRICALLY", "ACKNOWLEDGEABLE", "ACKNOWLEDGEABLY", "ACKNOWLEDGEMENT", "ACKNOWLEDGMENTS", "ACQUIRABILITIES", "ACQUISITIVENESS", "ACRIMONIOUSNESS", "ACROPARESTHESIA", "ACTINOBIOLOGIES", "ACTINOCHEMISTRY", "ACTINOTHERAPIES", "ADAPTABLENESSES", "ADDITIONALITIES", "ADENOCARCINOMAS", "ADENOHYPOPHYSES", "ADENOHYPOPHYSIS", "ADENOIDECTOMIES", "ADIATHERMANCIES", "ADJUSTABILITIES", "ADMINISTRATIONS", "ADMIRABLENESSES", "ADMISSIBILITIES", "ADRENALECTOMIES", "ADSORBABILITIES", "ADVENTUROUSNESS", "ADVERSARINESSES", "ADVISABLENESSES", "AERODYNAMICALLY", "AERODYNAMICISTS", "AEROELASTICIANS", "AEROHYDROPLANES", "AEROLITHOLOGIES", "AEROSOLISATIONS", "AEROSOLIZATIONS", "AFFECTABILITIES", "AFFECTIVENESSES", "AFFORDABILITIES", "AFFRANCHISEMENT", "AFTERSENSATIONS", "AGGLUTINABILITY", "AGGRANDISEMENTS", "AGGRANDIZEMENTS", "AGGREGATENESSES", "AGRANULOCYTOSES", "AGRANULOCYTOSIS", "AGREEABLENESSES", "AGRIBUSINESSMAN", "AGRIBUSINESSMEN", "AGRICULTURALIST", "AIRWORTHINESSES", "ALCOHOLISATIONS", "ALCOHOLIZATIONS", "ALCOHOLOMETRIES", "ALEXIPHARMAKONS", "ALGORITHMICALLY", "ALKALINISATIONS", "ALKALINIZATIONS", "ALLEGORICALNESS", "ALLEGORISATIONS", "ALLEGORIZATIONS", "ALLELOMORPHISMS", "ALLERGENICITIES", "ALLOTETRAPLOIDS", "ALLOTETRAPLOIDY", "ALLOTRIOMORPHIC", "ALLOWABLENESSES", "ALPHABETISATION", "ALPHABETIZATION", "ALTERNATIVENESS", "ALTITUDINARIANS", "ALUMINOSILICATE", "ALUMINOTHERMIES", "AMARYLLIDACEOUS", "AMBASSADORSHIPS", "AMBIDEXTERITIES", "AMBIGUOUSNESSES", "AMBISEXUALITIES", "AMBITIOUSNESSES", "AMINOPEPTIDASES", "AMINOPHENAZONES", "AMMONIFICATIONS", "AMORPHOUSNESSES", "AMPHIDIPLOIDIES", "AMPHITHEATRICAL", "ANACOLUTHICALLY", "ANACREONTICALLY", "ANAESTHESIOLOGY", "ANAESTHETICALLY", "ANAGRAMMATISING", "ANAGRAMMATIZING", "ANALOGOUSNESSES", "ANALYZABILITIES", "ANAMORPHOSCOPES", "ANCYLOSTOMIASES", "ANCYLOSTOMIASIS", "ANDROGYNOPHORES", "ANDROMEDOTOXINS", "ANDROMONOECIOUS", "ANDROMONOECISMS", "ANESTHETIZATION", "ANFRACTUOSITIES", "ANGUSTIROSTRATE", "ANIMATRONICALLY", "ANISOTROPICALLY", "ANKYLOSTOMIASES", "ANKYLOSTOMIASIS", "ANNIHILATIONISM", "ANOMALISTICALLY", "ANOMALOUSNESSES", "ANONYMOUSNESSES", "ANSWERABILITIES", "ANTAGONISATIONS", "ANTAGONIZATIONS", "ANTAPHRODISIACS", "ANTEPENULTIMATE", "ANTHROPOBIOLOGY", "ANTHROPOCENTRIC", "ANTHROPOGENESES", "ANTHROPOGENESIS", "ANTHROPOGENETIC", "ANTHROPOLATRIES", "ANTHROPOLOGICAL", "ANTHROPOLOGISTS", "ANTHROPOMETRIES", "ANTHROPOMETRIST", "ANTHROPOMORPHIC", "ANTHROPOPATHIES", "ANTHROPOPATHISM", "ANTHROPOPHAGIES", "ANTHROPOPHAGITE", "ANTHROPOPHAGOUS", "ANTHROPOPHOBIAS", "ANTHROPOPHOBICS", "ANTHROPOPHUISMS", "ANTHROPOPSYCHIC", "ANTHROPOSOPHIES", "ANTHROPOSOPHIST", "ANTIABORTIONIST", "ANTIALCOHOLISMS", "ANTIAPHRODISIAC", "ANTIARRHYTHMICS", "ANTICAPITALISMS", "ANTICAPITALISTS", "ANTICARCINOGENS", "ANTICHOLESTEROL", "ANTICHOLINERGIC", "ANTICHRISTIANLY", "ANTICLERICALISM", "ANTICLIMACTICAL", "ANTICOINCIDENCE", "ANTICOLONIALISM", "ANTICOLONIALIST", "ANTICOMPETITIVE", "ANTICONVULSANTS", "ANTICONVULSIVES", "ANTIDEPRESSANTS", "ANTIDERIVATIVES", "ANTIDEVELOPMENT", "ANTIEDUCATIONAL", "ANTIEGALITARIAN", "ANTIFASHIONABLE", "ANTIFEDERALISTS", "ANTIFERROMAGNET", "ANTIFORECLOSURE", "ANTIHELMINTHICS", "ANTIHISTAMINICS", "ANTILIBERALISMS", "ANTILIBERTARIAN", "ANTILOGARITHMIC", "ANTIMATERIALISM", "ANTIMATERIALIST", "ANTIMETABOLITES", "ANTIMILITARISMS", "ANTIMILITARISTS", "ANTIMONARCHICAL", "ANTIMONARCHISTS", "ANTIMONOPOLISTS", "ANTINATIONALIST", "ANTINUCLEARISTS", "ANTIODONTALGICS", "ANTIPERISTALSES", "ANTIPERISTALSIS", "ANTIPERISTALTIC", "ANTIPERSPIRANTS", "ANTIPHLOGISTICS", "ANTIPORNOGRAPHY", "ANTIPROGRESSIVE", "ANTIQUARIANISMS", "ANTIRADICALISMS", "ANTIRATIONALISM", "ANTIRATIONALIST", "ANTIRATIONALITY", "ANTIREPUBLICANS", "ANTIROMANTICISM", "ANTISEGREGATION", "ANTISENTIMENTAL", "ANTISEPARATISTS", "ANTISEPTICISING", "ANTISEPTICIZING", "ANTISEXUALITIES", "ANTISHOPLIFTING", "ANTISOCIALITIES", "ANTISPECULATION", "ANTISPECULATIVE", "ANTISYPHILITICS", "ANTITHEORETICAL", "ANTITHROMBOTICS", "ANTITRADITIONAL", "ANTITRANSPIRANT", "ANTITRINITARIAN", "ANTITUBERCULOUS", "ANTIVIVISECTION", "APHELIOTROPISMS", "APOCALYPTICALLY", "APOCALYPTICISMS", "APOLIPOPROTEINS", "APOLITICALITIES", "APOPHTHEGMATISE", "APOPHTHEGMATIST", "APOPHTHEGMATIZE", "APOTHEGMATISING", "APOTHEGMATIZING", "APPEALABILITIES", "APPEALINGNESSES", "APPENDICULARIAN", "APPLICABILITIES", "APPRENTICEHOODS", "APPRENTICEMENTS", "APPRENTICESHIPS", "APPROACHABILITY", "APPROPINQUATING", "APPROPINQUATION", "APPROPINQUITIES", "APPROPRIATENESS", "ARACHNOIDITISES", "ARBITRARINESSES", "ARBORICULTURIST", "ARCHAEBACTERIUM", "ARCHAEOBOTANIES", "ARCHAEOBOTANIST", "ARCHAEOMETRISTS", "ARCHAEOPTERYXES", "ARCHAEZOOLOGIES", "ARCHEOASTRONOMY", "ARCHEOBOTANISTS", "ARCHEOLOGICALLY", "ARCHEOMAGNETISM", "ARCHEOZOOLOGIES", "ARCHEOZOOLOGIST", "ARCHGENETHLIACS", "ARCHIDIACONATES", "ARCHIEPISCOPACY", "ARCHIEPISCOPATE", "ARCHITECTURALLY", "ARCHPRIESTHOODS", "ARCHPRIESTSHIPS", "ARGUMENTATIVELY", "ARIBOFLAVINOSES", "ARIBOFLAVINOSIS", "AROMATHERAPISTS", "ARRONDISSEMENTS", "ARTERIALISATION", "ARTERIALIZATION", "ARTERIOGRAPHIES", "ARTIFICIALISING", "ARTIFICIALITIES", "ARTIFICIALIZING", "ASCLEPIADACEOUS", "ASSENTIVENESSES"}
505+
506+
func BenchmarkCopy(b *testing.B) {
507+
for i := 0; i < b.N; i++ {
508+
CopyIn("temp", bigTableColumns...)
509+
}
510+
}

0 commit comments

Comments
 (0)