@@ -93,6 +93,7 @@ type ClickHouseCursor struct {
93
93
currentRow []interface {}
94
94
rowsReturned int64
95
95
limit int64
96
+ query SQLQueryToExecute
96
97
}
97
98
98
99
func (m * ClickHouseModule ) Create (c * sqlite3.SQLiteConn , args []string ) (sqlite3.VTab , error ) {
@@ -310,11 +311,44 @@ func (t *ClickHouseTable) Open() (sqlite3.VTabCursor, error) {
310
311
if err != nil {
311
312
return nil , fmt .Errorf ("error getting a new connection: %v" , err )
312
313
}
314
+
315
+ values := make ([]interface {}, len (t .schema ))
316
+ for i := range values {
317
+ values [i ] = new (interface {})
318
+ switch t .schema [i ].Type {
319
+ case "INTEGER" :
320
+ if t .schema [i ].RemoteType [0 ] == 'u' {
321
+ // For unsigned integers, we use NullUint64
322
+ values [i ] = new (NullUint64 ) // ClickHouse does not have unsigned integers, so we use NullInt64
323
+ } else {
324
+ values [i ] = new (sql.NullInt64 )
325
+ }
326
+ case "REAL" :
327
+ values [i ] = new (sql.NullFloat64 )
328
+ case "TEXT" :
329
+ if t .schema [i ].RemoteType == "ipv4" || t .schema [i ].RemoteType == "ipv6" {
330
+ // For IPv4 and IPv6, we use a net.IP type
331
+ values [i ] = new (net.IP )
332
+ } else {
333
+ values [i ] = new (sql.NullString )
334
+ }
335
+ case "BLOB" :
336
+ values [i ] = new ([]byte )
337
+ case "DATE" :
338
+ values [i ] = new (timeMySQL )
339
+ case "DATETIME" :
340
+ values [i ] = new (timeMySQL )
341
+ default :
342
+ values [i ] = new (interface {})
343
+ }
344
+ }
345
+
313
346
return & ClickHouseCursor {
314
347
connection : conn ,
315
348
tableName : t .tableName ,
316
349
schema : t .schema ,
317
350
limit : - 1 ,
351
+ currentRow : values ,
318
352
}, nil
319
353
}
320
354
@@ -341,7 +375,7 @@ func (t *ClickHouseTable) Destroy() error {
341
375
// To find the method, we will ask the database to explain the query and return the best method
342
376
func (t * ClickHouseTable ) BestIndex (cst []sqlite3.InfoConstraint , ob []sqlite3.InfoOrderBy , info sqlite3.IndexInformation ) (* sqlite3.IndexResult , error ) {
343
377
// Create the SQL query
344
- queryBuilder , limitCstIndex , offsetCstIndex , used := constructSQLQuery (cst , ob , t .schema , t .tableName )
378
+ queryBuilder , limitCstIndex , offsetCstIndex , used := efficientConstructSQLQuery (cst , ob , t .schema , t .tableName , info . ColUsed )
345
379
queryBuilder .SetFlavor (sqlbuilder .ClickHouse )
346
380
rawQuery , args := queryBuilder .Build ()
347
381
rawQuery += sqlQuerySuffix
@@ -391,6 +425,7 @@ func (t *ClickHouseTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.I
391
425
Args : args ,
392
426
LimitIndex : limitCstIndex ,
393
427
OffsetIndex : offsetCstIndex ,
428
+ ColumnsUsed : info .ColUsed ,
394
429
}
395
430
396
431
// Serialize the query as a JSON object
@@ -474,7 +509,6 @@ func (t *ClickHouseCursor) resetCursor() error {
474
509
t .limit = - 1
475
510
t .rowsReturned = 0
476
511
t .exhausted = false
477
- t .currentRow = nil
478
512
479
513
return nil
480
514
}
@@ -493,6 +527,9 @@ func (t *ClickHouseCursor) Filter(idxNum int, idxStr string, vals []interface{})
493
527
return fmt .Errorf ("error unmarshalling the query: %v" , err )
494
528
}
495
529
530
+ // Set the query for the cursor
531
+ t .query = query
532
+
496
533
// Get the LIMIT AND OFFSET values
497
534
// and remove them from the query so that we can pass these arguments to the query
498
535
limit := int64 (- 1 )
@@ -539,49 +576,22 @@ func (t *ClickHouseCursor) Next() error {
539
576
}
540
577
if hasMoreRows {
541
578
var err error
542
- // Init an array of the same size as the number of columns
543
- values := make ([]interface {}, len (t .schema ))
544
- for i := range values {
545
- values [i ] = new (interface {})
546
- switch t .schema [i ].Type {
547
- case "INTEGER" :
548
- if t .schema [i ].RemoteType [0 ] == 'u' {
549
- // For unsigned integers, we use NullUint64
550
- values [i ] = new (NullUint64 ) // ClickHouse does not have unsigned integers, so we use NullInt64
551
- } else {
552
- values [i ] = new (sql.NullInt64 )
553
- }
554
- case "REAL" :
555
- values [i ] = new (sql.NullFloat64 )
556
- case "TEXT" :
557
- if t .schema [i ].RemoteType == "ipv4" || t .schema [i ].RemoteType == "ipv6" {
558
- // For IPv4 and IPv6, we use a net.IP type
559
- values [i ] = new (net.IP )
560
- } else {
561
- values [i ] = new (sql.NullString )
562
- }
563
- case "BLOB" :
564
- values [i ] = new ([]byte )
565
- case "DATE" :
566
- values [i ] = new (timeMySQL )
567
- case "DATETIME" :
568
- values [i ] = new (timeMySQL )
569
- default :
570
- values [i ] = new (interface {})
579
+
580
+ dest := make ([]interface {}, 0 , len (t .schema ))
581
+ for i := range t .schema {
582
+ // ColumnsUsed is a bitmask that indicates which columns are used in the query
583
+ // If the last bit is set, it means that the rest of the columns are used
584
+ if t .query .ColumnsUsed & (1 << i ) == 0 && i < 62 {
585
+ continue
571
586
}
587
+
588
+ dest = append (dest , & t .currentRow [i ])
572
589
}
573
- err = t .rows .Scan (values ... )
590
+
591
+ err = t .rows .Scan (dest ... )
574
592
if err != nil {
575
593
return fmt .Errorf ("error scanning the row: %v" , err )
576
594
}
577
- t .currentRow = make ([]interface {}, len (values ))
578
- for i , v := range values {
579
- if v == nil {
580
- t .currentRow [i ] = nil
581
- continue
582
- }
583
- t .currentRow [i ] = v
584
- }
585
595
586
596
} else {
587
597
t .currentRow = nil
0 commit comments