@@ -2,6 +2,7 @@ const mysql = require('mysql');
22const genericPool = require ( 'generic-pool' ) ;
33const { promisify } = require ( 'util' ) ;
44const BaseDriver = require ( '@cubejs-backend/query-orchestrator/driver/BaseDriver' ) ;
5+ const crypto = require ( 'crypto' ) ;
56
67const GenericTypeToMySql = {
78 string : 'varchar(255) CHARACTER SET utf8mb4' ,
@@ -101,12 +102,15 @@ class MySqlDriver extends BaseDriver {
101102 }
102103
103104 query ( query , values ) {
104- const self = this ;
105- return this . withConnection ( db => db . execute ( `SET time_zone = '${ self . config . storeTimezone || '+00:00' } '` , [ ] )
105+ return this . withConnection ( db => this . setTimeZone ( db )
106106 . then ( ( ) => db . execute ( query , values ) )
107107 . then ( res => res ) ) ;
108108 }
109109
110+ setTimeZone ( db ) {
111+ return db . execute ( `SET time_zone = '${ this . config . storeTimezone || '+00:00' } '` , [ ] ) ;
112+ }
113+
110114 async release ( ) {
111115 await this . pool . drain ( ) ;
112116 await this . pool . clear ( ) ;
@@ -132,6 +136,27 @@ class MySqlDriver extends BaseDriver {
132136 return super . loadPreAggregationIntoTable ( preAggregationTableName , loadSql , params , tx ) ;
133137 }
134138
139+ async downloadQueryResults ( query , values ) {
140+ if ( ! this . config . database ) {
141+ throw new Error ( `Default database should be defined to be used for temporary tables during query results downloads` ) ;
142+ }
143+ const tableName = crypto . randomBytes ( 10 ) . toString ( 'hex' ) ;
144+ const columns = await this . withConnection ( async db => {
145+ await this . setTimeZone ( db ) ;
146+ await db . execute ( `CREATE TEMPORARY TABLE ${ this . config . database } .t_${ tableName } AS ${ query } LIMIT 0` , values ) ;
147+ const result = await db . execute ( `DESCRIBE ${ this . config . database } .t_${ tableName } ` ) ;
148+ await db . execute ( `DROP TEMPORARY TABLE ${ this . config . database } .t_${ tableName } ` ) ;
149+ return result ;
150+ } ) ;
151+
152+ const types = columns . map ( c => ( { name : c . Field , type : this . toGenericType ( c . Type ) } ) ) ;
153+
154+ return {
155+ rows : await this . query ( query , values ) ,
156+ types,
157+ } ;
158+ }
159+
135160 toColumnValue ( value , genericType ) {
136161 if ( genericType === 'timestamp' && typeof value === 'string' ) {
137162 return value && value . replace ( 'Z' , '' ) ;
0 commit comments