-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathprep_stmt.go
97 lines (84 loc) · 2.23 KB
/
prep_stmt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
AUTHOR
Grant Street Group <developers@grantstreet.com>
COPYRIGHT AND LICENSE
This software is Copyright (c) 2019 by Grant Street Group.
This is free software, licensed under:
MIT License
*/
package exasol
import (
"sort"
"time"
)
type prepStmt struct {
sth int
columns []column
lastUsed time.Time
}
func (c *Conn) getPrepStmt(schema, sql string) (*prepStmt, error) {
// TODO die if the num cols/rows expected by prepared statement
// doesn't match the passed in data (i.e. placeholder/binds mismatch)
// otherwise results in lowerlevel websocket closure
c.log.Debug("Preparing stmt for:", sql)
psc := c.prepStmtCache
ps := psc[sql]
if ps == nil {
var err error
ps, err = c.createPrepStmt(schema, sql)
if err != nil {
return nil, err
}
if c.Conf.CachePrepStmts {
psc[sql] = ps
c.Stats["StmtCacheLen"] = len(psc)
c.Stats["StmtCacheMiss"]++
}
}
ps.lastUsed = time.Now()
// Prune the prep stmt cache. I don't know how necessary it is
// but I saw something on the site about Exasol
// being unhappy if there are thousands of open statements.
if len(psc) > 1000 {
sortedStmts := make([]string, len(psc))
i := 0
for sql := range psc {
sortedStmts[i] = sql
i++
}
sort.Slice(sortedStmts, func(i, j int) bool {
return psc[sortedStmts[i]].lastUsed.Before(psc[sortedStmts[j]].lastUsed)
})
leastUsed := sortedStmts[0]
c.closePrepStmt(psc[leastUsed].sth)
delete(psc, leastUsed)
}
return ps, nil
}
func (c *Conn) createPrepStmt(schema string, sql string) (*prepStmt, error) {
sthReq := &createPrepStmtReq{
Command: "createPreparedStatement",
Attributes: &Attributes{CurrentSchema: schema},
SqlText: sql,
}
sthRes := &createPrepStmtRes{}
err := c.send(sthReq, sthRes)
if err != nil {
return nil, err
}
sth := sthRes.ResponseData.StatementHandle
cols := sthRes.ResponseData.ParameterData.Columns
return &prepStmt{sth, cols, time.Now()}, nil
}
func (c *Conn) closePrepStmt(sth int) error {
c.log.Debug("Closing stmt handle ", sth)
closeReq := &closePrepStmt{
Command: "closePreparedStatement",
StatementHandle: int(sth),
}
err := c.send(closeReq, &response{})
if err != nil {
return c.errorf("Unable to closePrepStmt: %s", err)
}
return nil
}