@@ -47,9 +47,11 @@ class DASSqliteTable(backend: DASSqliteBackend, defn: TableDefinition, maybePrim
47
47
*/
48
48
override def tableEstimate (quals : Seq [Qual ], columns : Seq [String ]): TableEstimate = {
49
49
// 1) Build the same WHERE clause used in `execute(...)`.
50
+ val supportedQuals = quals.flatMap(qualToSql)
51
+
50
52
val whereClause =
51
- if (quals .isEmpty) " "
52
- else " \n WHERE " + quals.map(qualToSql) .mkString(" AND " )
53
+ if (supportedQuals .isEmpty) " "
54
+ else " \n WHERE " + supportedQuals .mkString(" AND " )
53
55
54
56
// 2) Possibly use columns if you want to estimate only the subset of columns,
55
57
// or just use "*" or "1" to get an overall row count approximation.
@@ -210,9 +212,10 @@ class DASSqliteTable(backend: DASSqliteBackend, defn: TableDefinition, maybePrim
210
212
else columns.map(quoteIdentifier).mkString(" , " )
211
213
212
214
// Build WHERE from `quals`
215
+ val supportedQuals = quals.flatMap(qualToSql)
213
216
val whereClause =
214
- if (quals .isEmpty) " "
215
- else " \n WHERE " + quals.map(qualToSql) .mkString(" AND " )
217
+ if (supportedQuals .isEmpty) " "
218
+ else " \n WHERE " + supportedQuals .mkString(" AND " )
216
219
217
220
// Build ORDER BY
218
221
val orderByClause =
@@ -315,76 +318,81 @@ class DASSqliteTable(backend: DASSqliteBackend, defn: TableDefinition, maybePrim
315
318
str.replace(" '" , " ''" ) // naive approach for single quotes
316
319
317
320
/**
318
- * Maps an Operator enum to the corresponding SQL string. Some operators like ILIKE are not native to SQLite, so we
319
- * provide a naive fallback or throw an exception .
321
+ * Maps an Operator enum to the corresponding SQL string. Some operators like ILIKE are not native to SQLite, so we do
322
+ * not handle them .
320
323
*/
321
- private def operatorToSql (op : Operator ): String = {
324
+ private def operatorToSql (op : Operator ): Option [ String ] = {
322
325
op match {
323
- case Operator .EQUALS => " ="
324
- case Operator .NOT_EQUALS => " <>"
325
- case Operator .LESS_THAN => " <"
326
- case Operator .LESS_THAN_OR_EQUAL => " <="
327
- case Operator .GREATER_THAN => " >"
328
- case Operator .GREATER_THAN_OR_EQUAL => " >="
329
- case Operator .LIKE => " LIKE"
330
- case Operator .NOT_LIKE => " NOT LIKE"
331
-
332
- // SQLite does not have native ILIKE support. We can fallback to "LIKE" or fail.
333
- case Operator .ILIKE => throw new IllegalArgumentException (" SQLite does not support ILIKE." )
334
- case Operator .NOT_ILIKE => throw new IllegalArgumentException (" SQLite does not support NOT ILIKE." )
335
-
336
- // Arithmetic operators might not be typical in a WHERE Qual
337
- case Operator .PLUS => " +"
338
- case Operator .MINUS => " -"
339
- case Operator .TIMES => " *"
340
- case Operator .DIV => " /"
341
- case Operator .MOD => " %"
342
- case Operator .AND => " AND"
343
- case Operator .OR => " OR"
344
-
345
- case _ => throw new IllegalArgumentException (s " Unsupported operator: $op" )
326
+ case Operator .EQUALS => Some (" =" )
327
+ case Operator .NOT_EQUALS => Some (" <>" )
328
+ case Operator .LESS_THAN => Some (" <" )
329
+ case Operator .LESS_THAN_OR_EQUAL => Some (" <=" )
330
+ case Operator .GREATER_THAN => Some (" >" )
331
+ case Operator .GREATER_THAN_OR_EQUAL => Some (" >=" )
332
+ case Operator .LIKE => Some (" LIKE" )
333
+ case Operator .NOT_LIKE => Some (" NOT LIKE" )
334
+
335
+ // May be less typical in a WHERE clause
336
+ case Operator .PLUS => Some (" +" )
337
+ case Operator .MINUS => Some (" -" )
338
+ case Operator .TIMES => Some (" *" )
339
+ case Operator .DIV => Some (" /" )
340
+ case Operator .MOD => Some (" %" )
341
+ case Operator .AND => Some (" AND" )
342
+ case Operator .OR => Some (" OR" )
343
+
344
+ case _ => None
346
345
}
347
346
}
348
347
349
348
/**
350
349
* `IsAllQual` means "col op ALL these values", we interpret as multiple AND clauses
351
350
*/
352
- private def isAllQualToSql (colName : String , iq : IsAllQual ): String = {
353
- val opStr = operatorToSql(iq.getOperator)
354
- val clauses = iq.getValuesList.asScala.map(v => s " $colName $opStr ${valueToSql(v)}" )
355
- // Combine with AND
356
- clauses.mkString(" (" , " AND " , " )" )
351
+ private def isAllQualToSql (colName : String , iq : IsAllQual ): Option [String ] = {
352
+ operatorToSql(iq.getOperator) match {
353
+ case Some (opStr) =>
354
+ val clauses = iq.getValuesList.asScala.map(v => s " $colName $opStr ${valueToSql(v)}" )
355
+ // Combine with AND
356
+ Some (clauses.mkString(" (" , " AND " , " )" ))
357
+ case None => None
358
+ }
357
359
}
358
360
359
361
/**
360
362
* `IsAnyQual` means "col op ANY of these values", we interpret as multiple OR clauses
361
363
*/
362
- private def isAnyQualToSql (colName : String , iq : IsAnyQual ): String = {
363
- val opStr = operatorToSql(iq.getOperator)
364
- val clauses = iq.getValuesList.asScala.map(v => s " $colName $opStr ${valueToSql(v)}" )
365
- // Combine with OR
366
- clauses.mkString(" (" , " OR " , " )" )
364
+ private def isAnyQualToSql (colName : String , iq : IsAnyQual ): Option [String ] = {
365
+ operatorToSql(iq.getOperator) match {
366
+ case Some (opStr) =>
367
+ val clauses = iq.getValuesList.asScala.map(v => s " $colName $opStr ${valueToSql(v)}" )
368
+ // Combine with OR
369
+ Some (clauses.mkString(" (" , " OR " , " )" ))
370
+ case None => None
371
+ }
367
372
}
368
373
369
374
/**
370
375
* `SimpleQual` is a single condition: "col op value"
371
376
*/
372
- private def simpleQualToSql (colName : String , sq : SimpleQual ): String = {
377
+ private def simpleQualToSql (colName : String , sq : SimpleQual ): Option [ String ] = {
373
378
if (sq.getValue.hasNull && sq.getOperator == Operator .EQUALS ) {
374
- s " $colName IS NULL "
379
+ Some ( s " $colName IS NULL " )
375
380
} else if (sq.getValue.hasNull && sq.getOperator == Operator .NOT_EQUALS ) {
376
- s " $colName IS NOT NULL "
381
+ Some ( s " $colName IS NOT NULL " )
377
382
} else {
378
- val opStr = operatorToSql(sq.getOperator)
379
- val valStr = valueToSql(sq.getValue)
380
- s " $colName $opStr $valStr"
383
+ operatorToSql(sq.getOperator) match {
384
+ case Some (opStr) =>
385
+ val valStr = valueToSql(sq.getValue)
386
+ Some (s " $colName $opStr $valStr" )
387
+ case None => None
388
+ }
381
389
}
382
390
}
383
391
384
392
/**
385
393
* Converts any `Qual` to a SQL snippet. We handle `SimpleQual`, `IsAnyQual`, or `IsAllQual`.
386
394
*/
387
- private def qualToSql (q : Qual ): String = {
395
+ private def qualToSql (q : Qual ): Option [ String ] = {
388
396
val colName = quoteIdentifier(q.getName)
389
397
if (q.hasSimpleQual) {
390
398
simpleQualToSql(colName, q.getSimpleQual)
0 commit comments