@@ -6,8 +6,11 @@ import (
6
6
"database/sql"
7
7
"database/sql/driver"
8
8
"fmt"
9
+ "math/rand"
10
+ "os"
9
11
"strconv"
10
12
"strings"
13
+ "time"
11
14
12
15
"github.com/chdb-io/chdb-go/chdb"
13
16
"github.com/chdb-io/chdb-go/chdbstable"
@@ -34,6 +37,10 @@ const (
34
37
defaultBufferSize = 512
35
38
)
36
39
40
+ const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
41
+
42
+ var seededRand * rand.Rand = rand .New (rand .NewSource (time .Now ().UnixNano ()))
43
+
37
44
func (d DriverType ) String () string {
38
45
switch d {
39
46
case ARROW :
@@ -46,20 +53,51 @@ func (d DriverType) String() string {
46
53
return ""
47
54
}
48
55
49
- func (d DriverType ) PrepareRows (result * chdbstable.LocalResult , buf []byte , bufSize int , useUnsafe bool ) (driver.Rows , error ) {
56
+ func (d DriverType ) PrepareRows (result * chdbstable.LocalResult , buf []byte , bufSize int , useUnsafe bool , filePath string ) (driver.Rows , error ) {
50
57
switch d {
51
58
case ARROW :
52
- reader , err := ipc .NewFileReader (bytes .NewReader (buf ))
53
- if err != nil {
54
- return nil , err
59
+ var reader * ipc.FileReader
60
+ var err error
61
+ var fd * os.File
62
+ if filePath != "" {
63
+ fd , err = os .Open (filePath )
64
+ if err != nil {
65
+ return nil , err
66
+ }
67
+
68
+ reader , err = ipc .NewFileReader (fd )
69
+ if err != nil {
70
+ return nil , err
71
+ }
72
+
73
+ } else {
74
+ reader , err = ipc .NewFileReader (bytes .NewReader (buf ))
75
+ if err != nil {
76
+ return nil , err
77
+ }
55
78
}
56
- return & arrowRows {localResult : result , reader : reader }, nil
79
+
80
+ return & arrowRows {localResult : result , reader : reader , fd : fd }, nil
57
81
case PARQUET :
58
- reader := parquet.NewGenericReader [any ](bytes .NewReader (buf ))
82
+ var reader * parquet.GenericReader [any ]
83
+ var fd * os.File
84
+ if filePath != "" {
85
+ fl , err := os .Open (filePath )
86
+ if err != nil {
87
+ return nil , err
88
+ }
89
+ fd = fl
90
+
91
+ reader = parquet.NewGenericReader [any ](fl )
92
+ } else {
93
+ reader = parquet.NewGenericReader [any ](bytes .NewReader (buf ))
94
+ }
95
+
59
96
return & parquetRows {
60
97
localResult : result , reader : reader ,
61
98
bufferSize : bufSize , needNewBuffer : true ,
62
99
useUnsafeStringReader : useUnsafe ,
100
+ fd : fd ,
63
101
}, nil
64
102
}
65
103
return nil , fmt .Errorf ("Unsupported driver type" )
@@ -97,7 +135,8 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
97
135
cc := & conn {
98
136
udfPath : c .udfPath , session : c .session ,
99
137
driverType : c .driverType , bufferSize : c .bufferSize ,
100
- useUnsafe : c .useUnsafe ,
138
+ useUnsafe : c .useUnsafe ,
139
+ useFileInsteadOfMemory : true ,
101
140
}
102
141
cc .SetupQueryFun ()
103
142
return cc , nil
@@ -184,12 +223,13 @@ func (d Driver) OpenConnector(name string) (driver.Connector, error) {
184
223
}
185
224
186
225
type conn struct {
187
- udfPath string
188
- driverType DriverType
189
- bufferSize int
190
- useUnsafe bool
191
- session * chdb.Session
192
- QueryFun queryHandle
226
+ udfPath string
227
+ driverType DriverType
228
+ bufferSize int
229
+ useUnsafe bool
230
+ useFileInsteadOfMemory bool
231
+ session * chdb.Session
232
+ QueryFun queryHandle
193
233
}
194
234
195
235
func (c * conn ) Close () error {
@@ -230,14 +270,31 @@ func (c *conn) compileArguments(query string, args []driver.NamedValue) (string,
230
270
} else {
231
271
compiledQuery = query
232
272
}
273
+
233
274
return compiledQuery , nil
234
275
}
235
276
277
+ func (c * conn ) createRandomFilePath (size int ) string {
278
+ b := make ([]byte , size )
279
+ for i := range b {
280
+ b [i ] = charset [seededRand .Intn (len (charset ))]
281
+ }
282
+ return string (b )
283
+
284
+ }
285
+
236
286
func (c * conn ) QueryContext (ctx context.Context , query string , args []driver.NamedValue ) (driver.Rows , error ) {
237
287
compiledQuery , err := c .compileArguments (query , args )
238
288
if err != nil {
239
289
return nil , err
240
290
}
291
+ var filePath string
292
+ if c .useFileInsteadOfMemory {
293
+ compiledQuery = strings .TrimSuffix (compiledQuery , ";" )
294
+ compiledQuery += " INTO OUTFILE "
295
+ filePath = fmt .Sprintf ("/tmp/%s.%s" , c .createRandomFilePath (16 ), strings .ToLower (c .driverType .String ()))
296
+ compiledQuery += fmt .Sprintf ("'%s'" , filePath )
297
+ }
241
298
result , err := c .QueryFun (compiledQuery , c .driverType .String (), c .udfPath )
242
299
if err != nil {
243
300
return nil , err
@@ -247,7 +304,7 @@ func (c *conn) QueryContext(ctx context.Context, query string, args []driver.Nam
247
304
if buf == nil {
248
305
return nil , fmt .Errorf ("result is nil" )
249
306
}
250
- return c .driverType .PrepareRows (result , buf , c .bufferSize , c .useUnsafe )
307
+ return c .driverType .PrepareRows (result , buf , c .bufferSize , c .useUnsafe , filePath )
251
308
252
309
}
253
310
0 commit comments