@@ -38,6 +38,7 @@ import (
3838 "github.com/google/go-cmp/cmp/cmpopts"
3939 "github.com/google/uuid"
4040 "github.com/googleapis/go-sql-spanner/testutil"
41+ lru "github.com/hashicorp/golang-lru"
4142 "google.golang.org/api/option"
4243 "google.golang.org/grpc"
4344 "google.golang.org/grpc/metadata"
@@ -72,6 +73,117 @@ func TestPingContext_Fails(t *testing.T) {
7273 }
7374}
7475
76+ func TestStatementCacheSize (t * testing.T ) {
77+ t .Parallel ()
78+
79+ db , server , teardown := setupTestDBConnectionWithParams (t , "StatementCacheSize=2" )
80+ defer teardown ()
81+
82+ c , err := db .Conn (context .Background ())
83+ if err != nil {
84+ t .Fatalf ("unexpected error for connection: %v" , err )
85+ }
86+ var cache * lru.Cache
87+ if err := c .Raw (func (driverConn any ) error {
88+ sc , ok := driverConn .(* conn )
89+ if ! ok {
90+ return fmt .Errorf ("driverConn is not a Spanner conn" )
91+ }
92+ cache = sc .parser .statementsCache
93+ return nil
94+ }); err != nil {
95+ t .Fatalf ("unexpected error for raw: %v" , err )
96+ }
97+
98+ // The cache should initially be empty.
99+ if g , w := cache .Len (), 0 ; g != w {
100+ t .Fatalf ("cache size mismatch\n Got: %v\n Want: %v" , g , w )
101+ }
102+
103+ for n := 0 ; n < 3 ; n ++ {
104+ rows , err := db .QueryContext (context .Background (), testutil .SelectFooFromBar )
105+ if err != nil {
106+ t .Fatal (err )
107+ }
108+ for rows .Next () {
109+ }
110+ if err := rows .Close (); err != nil {
111+ t .Fatal (err )
112+ }
113+ }
114+
115+ // Executing the same query multiple times should add the statement once to the cache.
116+ if g , w := cache .Len (), 1 ; g != w {
117+ t .Fatalf ("cache size mismatch\n Got: %v\n Want: %v" , g , w )
118+ }
119+
120+ // Executing another statement should add yet another statement to the cache.
121+ if _ , err := db .ExecContext (context .Background (), testutil .UpdateBarSetFoo ); err != nil {
122+ t .Fatal (err )
123+ }
124+ if g , w := cache .Len (), 2 ; g != w {
125+ t .Fatalf ("cache size mismatch\n Got: %v\n Want: %v" , g , w )
126+ }
127+
128+ // Executing yet another statement should evict the oldest result from the cache and add this.
129+ query := "insert into test (id) values (1)"
130+ server .TestSpanner .PutStatementResult (query , & testutil.StatementResult {
131+ Type : testutil .StatementResultUpdateCount ,
132+ UpdateCount : 1 ,
133+ })
134+ if _ , err := db .ExecContext (context .Background (), query ); err != nil {
135+ t .Fatal (err )
136+ }
137+ // The cache size should still be 2.
138+ if g , w := cache .Len (), 2 ; g != w {
139+ t .Fatalf ("cache size mismatch\n Got: %v\n Want: %v" , g , w )
140+ }
141+ }
142+
143+ func TestDisableStatementCache (t * testing.T ) {
144+ t .Parallel ()
145+
146+ db , _ , teardown := setupTestDBConnectionWithParams (t , "DisableStatementCache=true" )
147+ defer teardown ()
148+
149+ c , err := db .Conn (context .Background ())
150+ if err != nil {
151+ t .Fatalf ("unexpected error for connection: %v" , err )
152+ }
153+ var cache * lru.Cache
154+ if err := c .Raw (func (driverConn any ) error {
155+ sc , ok := driverConn .(* conn )
156+ if ! ok {
157+ return fmt .Errorf ("driverConn is not a Spanner conn" )
158+ }
159+ cache = sc .parser .statementsCache
160+ return nil
161+ }); err != nil {
162+ t .Fatalf ("unexpected error for raw: %v" , err )
163+ }
164+
165+ // There should be no cache.
166+ if cache != nil {
167+ t .Fatalf ("statement cache should be disabled" )
168+ }
169+
170+ // Executing queries and other statements should work without a cache.
171+ for n := 0 ; n < 3 ; n ++ {
172+ rows , err := db .QueryContext (context .Background (), testutil .SelectFooFromBar )
173+ if err != nil {
174+ t .Fatal (err )
175+ }
176+ for rows .Next () {
177+ }
178+ if err := rows .Close (); err != nil {
179+ t .Fatal (err )
180+ }
181+ }
182+ if _ , err := db .ExecContext (context .Background (), testutil .UpdateBarSetFoo ); err != nil {
183+ t .Fatal (err )
184+ }
185+ }
186+
75187func TestSimpleQuery (t * testing.T ) {
76188 t .Parallel ()
77189
0 commit comments