@@ -89,13 +89,21 @@ export type ExecuteQueryOptions<TOut extends z.ZodSchema> = Omit<
8989 customOrgConcurrencyLimit ?: number ;
9090} ;
9191
92+ /**
93+ * Extended result type that includes the optional queryId when saved to history
94+ */
95+ export type ExecuteQueryResult < T > =
96+ | [ error : Error , result : null , queryId : null ]
97+ | [ error : null , result : T , queryId : string | null ] ;
98+
9299/**
93100 * Execute a TSQL query against ClickHouse with tenant isolation
94101 * Handles building tenant options, field mappings, and optionally saves to history
102+ * Returns [error, result, queryId] where queryId is the CustomerQuery ID if saved to history
95103 */
96104export async function executeQuery < TOut extends z . ZodSchema > (
97105 options : ExecuteQueryOptions < TOut >
98- ) : Promise < TSQLQueryResult < z . output < TOut > > > {
106+ ) : Promise < ExecuteQueryResult < Exclude < TSQLQueryResult < z . output < TOut > > [ 1 ] , null > > > {
99107 const {
100108 scope,
101109 organizationId,
@@ -112,20 +120,20 @@ export async function executeQuery<TOut extends z.ZodSchema>(
112120 const orgLimit = customOrgConcurrencyLimit ?? DEFAULT_ORG_CONCURRENCY_LIMIT ;
113121
114122 // Acquire concurrency slot
115- const acquireResult = await queryConcurrencyLimiter . acquire ( {
116- key : organizationId ,
117- requestId,
118- keyLimit : orgLimit ,
119- globalLimit : GLOBAL_CONCURRENCY_LIMIT ,
120- } ) ;
121-
122- if ( ! acquireResult . success ) {
123- const errorMessage =
124- acquireResult . reason === "key_limit"
125- ? `You've exceeded your query concurrency of ${ orgLimit } for this organization. Please try again later.`
126- : "We're experiencing a lot of queries at the moment. Please try again later." ;
127- return [ new QueryError ( errorMessage , { query : options . query } ) , null ] ;
128- }
123+ const acquireResult = await queryConcurrencyLimiter . acquire ( {
124+ key : organizationId ,
125+ requestId,
126+ keyLimit : orgLimit ,
127+ globalLimit : GLOBAL_CONCURRENCY_LIMIT ,
128+ } ) ;
129+
130+ if ( ! acquireResult . success ) {
131+ const errorMessage =
132+ acquireResult . reason === "key_limit"
133+ ? `You've exceeded your query concurrency of ${ orgLimit } for this organization. Please try again later.`
134+ : "We're experiencing a lot of queries at the moment. Please try again later." ;
135+ return [ new QueryError ( errorMessage , { query : options . query } ) , null , null ] ;
136+ }
129137
130138 try {
131139 // Build tenant IDs based on scope
@@ -172,9 +180,16 @@ export async function executeQuery<TOut extends z.ZodSchema>(
172180 } ,
173181 } ) ;
174182
183+ // If query failed, return early with no queryId
184+ if ( result [ 0 ] !== null ) {
185+ return [ result [ 0 ] , null , null ] ;
186+ }
187+
188+ let queryId : string | null = null ;
189+
175190 // If query succeeded and history options provided, save to history
176191 // Skip history for EXPLAIN queries (admin debugging) and when explicitly skipped (e.g., impersonating)
177- if ( result [ 0 ] === null && history && ! history . skip && ! baseOptions . explain ) {
192+ if ( history && ! history . skip && ! baseOptions . explain ) {
178193 // Check if this query is the same as the last one saved (avoid duplicate history entries)
179194 const lastQuery = await prisma . customerQuery . findFirst ( {
180195 where : {
@@ -183,7 +198,7 @@ export async function executeQuery<TOut extends z.ZodSchema>(
183198 userId : history . userId ?? null ,
184199 } ,
185200 orderBy : { createdAt : "desc" } ,
186- select : { query : true , scope : true , filterPeriod : true , filterFrom : true , filterTo : true } ,
201+ select : { id : true , query : true , scope : true , filterPeriod : true , filterFrom : true , filterTo : true } ,
187202 } ) ;
188203
189204 const timeFilter = history . timeFilter ;
@@ -195,8 +210,11 @@ export async function executeQuery<TOut extends z.ZodSchema>(
195210 lastQuery . filterFrom ?. getTime ( ) === ( timeFilter ?. from ?. getTime ( ) ?? undefined ) &&
196211 lastQuery . filterTo ?. getTime ( ) === ( timeFilter ?. to ?. getTime ( ) ?? undefined ) ;
197212
198- if ( ! isDuplicate ) {
199- await prisma . customerQuery . create ( {
213+ if ( isDuplicate && lastQuery ) {
214+ // Return the existing query's ID for duplicate queries
215+ queryId = lastQuery . id ;
216+ } else {
217+ const created = await prisma . customerQuery . create ( {
200218 data : {
201219 query : options . query ,
202220 scope : scopeToEnum [ scope ] ,
@@ -211,10 +229,11 @@ export async function executeQuery<TOut extends z.ZodSchema>(
211229 filterTo : history . timeFilter ?. to ?? null ,
212230 } ,
213231 } ) ;
232+ queryId = created . id ;
214233 }
215234 }
216235
217- return result ;
236+ return [ null , result [ 1 ] , queryId ] ;
218237 } finally {
219238 // Always release the concurrency slot
220239 await queryConcurrencyLimiter . release ( {
0 commit comments