1 10 11 package com.triactive.jdo.store; 12 13 import com.triactive.jdo.PersistenceManager; 14 import java.math.BigInteger ; 15 import java.math.BigDecimal ; 16 import java.sql.Connection ; 17 import java.sql.PreparedStatement ; 18 import java.sql.ResultSet ; 19 import java.sql.SQLException ; 20 import java.util.ArrayList ; 21 import java.util.Collection ; 22 import java.util.HashMap ; 23 import java.util.Map ; 24 import java.util.StringTokenizer ; 25 import javax.jdo.Extent; 26 import javax.jdo.JDODataStoreException; 27 import javax.jdo.JDOFatalInternalException; 28 import javax.jdo.JDOUnsupportedOptionException; 29 import javax.jdo.JDOUserException; 30 import org.apache.log4j.Category; 31 32 33 41 42 public class JDOQLQuery extends Query 43 { 44 private static final Category LOG = Category.getInstance(JDOQLQuery.class); 45 private static final Category CLOG = Category.getInstance(Compiler .class); 46 47 private transient Queryable candidates = null; 48 49 private transient QueryStatement queryStmt = null; 50 private transient Query.ResultObjectFactory rof = null; 51 52 53 58 59 public JDOQLQuery() 60 { 61 this(null, null); 62 } 63 64 65 70 71 public JDOQLQuery(PersistenceManager pm, StoreManager storeMgr) 72 { 73 this(pm, storeMgr, null); 74 } 75 76 77 83 84 public JDOQLQuery(PersistenceManager pm, StoreManager storeMgr, JDOQLQuery q) 85 { 86 super(pm, storeMgr); 87 88 if (q == null) 89 { 90 candidateClass = null; 91 filter = null; 92 imports = null; 93 variables = null; 94 parameters = null; 95 ordering = null; 96 } 97 else 98 { 99 candidateClass = q.candidateClass; 100 filter = q.filter; 101 imports = q.imports; 102 variables = q.variables; 103 parameters = q.parameters; 104 ordering = q.ordering; 105 } 106 } 107 108 109 protected void discardCompiled() 110 { 111 super.discardCompiled(); 112 113 queryStmt = null; 114 rof = null; 115 } 116 117 118 public boolean equals(Object obj) 119 { 120 if (obj == this) 121 return true; 122 123 if (!(obj instanceof JDOQLQuery) || !super.equals(obj)) 124 return false; 125 126 return true; 127 } 128 129 130 137 138 public void setCandidates(Extent pcs) 139 { 140 setCandidatesInternal(pcs); 141 } 142 143 144 151 152 public void setCandidates(Collection pcs) 153 { 154 setCandidatesInternal(pcs); 155 } 156 157 158 private void setCandidatesInternal(Object pcs) 159 { 160 if (!(pcs instanceof Queryable)) 161 throw new JDOUnsupportedOptionException("Object not queryable: " + pcs.getClass().getName()); 162 163 discardCompiled(); 164 165 candidates = (Queryable)pcs; 166 167 if (candidateClass == null) 168 candidateClass = candidates.getCandidateClass(); 169 } 170 171 172 178 179 public void compile() 180 { 181 super.compile(); 182 isCompiled = true; 183 } 184 185 186 private void compile(Map parameters) 187 { 188 discardCompiled(); 189 super.compile(); 190 191 try 192 { 193 if (CLOG.isDebugEnabled()) 194 { 195 CLOG.debug("Filter = " + filter); 196 CLOG.debug("Ordering = " + ordering); 197 } 198 199 long startTime = System.currentTimeMillis(); 200 201 Compiler c = new Compiler (parameters); 202 203 queryStmt = c.compileQueryStatement(); 204 rof = candidates.newResultObjectFactory(queryStmt); 205 206 if (CLOG.isDebugEnabled()) 207 CLOG.debug("Compile time = " + (System.currentTimeMillis() - startTime) + " ms: " + queryStmt); 208 209 isCompiled = true; 210 } 211 finally 212 { 213 if (!isCompiled) 214 discardCompiled(); 215 } 216 } 217 218 219 229 230 public Object executeWithMap(Map parameters) 231 { 232 compile(parameters); 233 234 QueryResult qr = null; 235 236 try 237 { 238 Connection conn = pm.getConnection(false); 239 240 try 241 { 242 PreparedStatement ps = queryStmt.toStatementText().prepareStatement(pm, conn); 243 244 try 245 { 246 long startTime = System.currentTimeMillis(); 247 248 ResultSet rs = ps.executeQuery(); 249 250 if (LOG.isDebugEnabled()) 251 LOG.debug("Time = " + (System.currentTimeMillis() - startTime) + " ms: " + queryStmt); 252 253 try 254 { 255 qr = new QueryResult(this, rof, rs); 256 } 257 finally 258 { 259 if (qr == null) 260 rs.close(); 261 } 262 } 263 finally 264 { 265 if (qr == null) 266 ps.close(); 267 } 268 } 269 finally 270 { 271 pm.releaseConnection(conn); 272 } 273 } 274 catch (SQLException e) 275 { 276 throw dba.newDataStoreException("Error executing query: " + queryStmt, e); 277 } 278 279 queryResults.add(qr); 280 281 return qr; 282 } 283 284 285 public class Compiler 286 { 287 private final Map parameters; 288 private final HashMap expressionsByVariableName = new HashMap (); 289 private QueryStatement qs = null; 290 private Parser p = null; 291 292 293 public Compiler(Map parameters) 294 { 295 if (parameters != null && parameters.size() != parameterNames.size()) 296 throw new JDOUserException("Incorrect number of parameters: " + parameters.size() + ", s/b " + parameterNames.size()); 297 298 this.parameters = parameters; 299 } 300 301 302 public void bindVariable(String name, SQLExpression expr) 303 { 304 SQLExpression previousExpr = (SQLExpression)expressionsByVariableName.put(name, expr); 305 306 if (previousExpr != null) 307 throw new JDOFatalInternalException("Error binding " + name + " to " + expr + ", previously bound to " + previousExpr); 308 } 309 310 311 public QueryStatement compileQueryStatement() 312 { 313 if (candidates == null) 314 { 315 if (candidateClass == null) 316 throw new JDOUserException("No candidate class provided"); 317 318 candidates = (Queryable)pm.getExtent(candidateClass, true); 319 } 320 321 qs = candidates.newQueryStatement(candidateClass); 322 323 if (filter != null && filter.length() > 0) 324 { 325 p = new Parser(filter, parsedImports); 326 SQLExpression expr = compileExpression(); 327 328 if (!p.parseEOS()) 329 throw new ExpressionSyntaxException("Invalid expression"); 330 331 if (!(expr instanceof BooleanExpression)) 332 throw new JDOUserException("Filter expression does not yield boolean result: " + filter); 333 334 qs.andCondition((BooleanExpression)expr); 335 } 336 337 if (ordering != null && ordering.length() > 0) 338 { 339 StringTokenizer t1 = new StringTokenizer (ordering, ","); 340 341 int n = t1.countTokens(); 342 SQLExpression[] orderExprs = new SQLExpression[n]; 343 boolean[] descending = new boolean[n]; 344 345 for (n = 0; t1.hasMoreTokens(); ++n) 346 { 347 StringTokenizer t2 = new StringTokenizer (t1.nextToken(), " "); 348 349 if (t2.countTokens() != 2) 350 throw new JDOUserException("Invalid order specification: " + ordering); 351 352 String expression = t2.nextToken(); 353 String direction = t2.nextToken(); 354 355 p = new Parser(expression, parsedImports); 356 orderExprs[n] = compileExpression(); 357 358 if (!p.parseEOS()) 359 throw new ExpressionSyntaxException("Invalid expression"); 360 361 if (direction.equals("ascending")) 362 descending[n] = false; 363 else if (direction.equals("descending")) 364 descending[n] = true; 365 else 366 throw new JDOUserException("Invalid order direction: " + ordering); 367 } 368 369 qs.setOrdering(orderExprs, descending); 370 } 371 372 return qs; 373 } 374 375 376 private SQLExpression compileExpression() 377 { 378 return compileConditionalOrExpression(); 379 } 380 381 382 private SQLExpression compileConditionalOrExpression() 383 { 384 SQLExpression expr = compileConditionalAndExpression(); 385 386 while (p.parseString("||")) 387 expr = expr.ior(compileConditionalAndExpression()); 388 389 return expr; 390 } 391 392 393 private SQLExpression compileConditionalAndExpression() 394 { 395 SQLExpression expr = compileInclusiveOrExpression(); 396 397 while (p.parseString("&&")) 398 expr = expr.and(compileInclusiveOrExpression()); 399 400 return expr; 401 } 402 403 404 private SQLExpression compileInclusiveOrExpression() 405 { 406 SQLExpression expr = compileExclusiveOrExpression(); 407 408 while (p.parseChar('|', '|')) 409 expr = expr.ior(compileExclusiveOrExpression()); 410 411 return expr; 412 } 413 414 415 private SQLExpression compileExclusiveOrExpression() 416 { 417 SQLExpression expr = compileAndExpression(); 418 419 while (p.parseChar('^')) 420 expr = expr.eor(compileExclusiveOrExpression()); 421 422 return expr; 423 } 424 425 426 private SQLExpression compileAndExpression() 427 { 428 SQLExpression expr = compileEqualityExpression(); 429 430 while (p.parseChar('&', '&')) 431 expr = expr.and(compileEqualityExpression()); 432 433 return expr; 434 } 435 436 437 private SQLExpression compileEqualityExpression() 438 { 439 SQLExpression expr = compileRelationalExpression(); 440 441 for (;;) 442 { 443 if (p.parseString("==")) 444 expr = expr.eq(compileRelationalExpression()); 445 else if (p.parseString("!=")) 446 expr = expr.noteq(compileRelationalExpression()); 447 else 448 break; 449 } 450 451 return expr; 452 } 453 454 455 private SQLExpression compileRelationalExpression() 456 { 457 SQLExpression expr = compileAdditiveExpression(); 458 459 for (;;) 460 { 461 if (p.parseString("<=")) 462 expr = expr.lteq(compileAdditiveExpression()); 463 else if (p.parseString(">=")) 464 expr = expr.gteq(compileAdditiveExpression()); 465 else if (p.parseChar('<')) 466 expr = expr.lt(compileAdditiveExpression()); 467 else if (p.parseChar('>')) 468 expr = expr.gt(compileAdditiveExpression()); 469 else 470 break; 471 } 472 473 return expr; 474 } 475 476 477 private SQLExpression compileAdditiveExpression() 478 { 479 SQLExpression expr = compileMultiplicativeExpression(); 480 481 for (;;) 482 { 483 if (p.parseChar('+')) 484 expr = expr.add(compileMultiplicativeExpression()); 485 else if (p.parseChar('-')) 486 expr = expr.sub(compileMultiplicativeExpression()); 487 else 488 break; 489 } 490 491 return expr; 492 } 493 494 495 private SQLExpression compileMultiplicativeExpression() 496 { 497 SQLExpression expr = compileUnaryExpression(); 498 499 for (;;) 500 { 501 if (p.parseChar('*')) 502 expr = expr.mul(compileUnaryExpression()); 503 else if (p.parseChar('/')) 504 expr = expr.div(compileUnaryExpression()); 505 else if (p.parseChar('%')) 506 expr = expr.mod(compileUnaryExpression()); 507 else 508 break; 509 } 510 511 return expr; 512 } 513 514 515 private SQLExpression compileUnaryExpression() 516 { 517 SQLExpression expr; 518 519 if (p.parseChar('+')) 520 expr = compileUnaryExpression(); 521 else if (p.parseChar('-')) 522 expr = compileUnaryExpression().neg(); 523 else 524 expr = compileUnaryExpressionNotPlusMinus(); 525 526 return expr; 527 } 528 529 530 private SQLExpression compileUnaryExpressionNotPlusMinus() 531 { 532 SQLExpression expr; 533 534 if (p.parseChar('~')) 535 expr = compileUnaryExpression().com(); 536 else if (p.parseChar('!')) 537 expr = compileUnaryExpression().not(); 538 else if ((expr = compileCastExpression()) == null) 539 expr = compilePrimary(); 540 541 return expr; 542 } 543 544 545 private SQLExpression compileCastExpression() 546 { 547 Class type; 548 549 if ((type = p.parseCast()) == null) 550 return null; 551 552 return compileUnaryExpression().cast(type); 553 } 554 555 556 private SQLExpression compilePrimary() 557 { 558 SQLExpression expr = compileLiteral(); 559 560 if (expr == null) 561 { 562 if (p.parseChar('(')) 563 { 564 expr = compileExpression(); 565 566 if (!p.parseChar(')')) 567 throw new ExpressionSyntaxException("')' expected"); 568 } 569 else 570 expr = compileIdentifier(); 571 572 while (p.parseChar('.')) 573 { 574 String id = p.parseIdentifier(); 575 576 if (id == null) 577 throw new ExpressionSyntaxException("Identifier expected"); 578 579 if (p.parseChar('(')) 580 { 581 ArrayList args = new ArrayList (); 582 583 if (!p.parseChar(')')) 584 { 585 do 586 { 587 args.add(compileExpression()); 588 } while (p.parseChar(',')); 589 590 if (!p.parseChar(')')) 591 throw new ExpressionSyntaxException("')' expected"); 592 } 593 594 expr = expr.callMethod(id, args); 595 } 596 else 597 expr = expr.accessField(id); 598 } 599 } 600 601 return expr; 602 } 603 604 605 private SQLExpression compileLiteral() 606 { 607 Class litType; 608 Object litValue; 609 610 String sLiteral; 611 BigDecimal fLiteral; 612 BigInteger iLiteral; 613 Character cLiteral; 614 Boolean bLiteral; 615 616 if ((sLiteral = p.parseStringLiteral()) != null) 617 { 618 litType = String .class; 619 litValue = sLiteral; 620 } 621 else if ((fLiteral = p.parseFloatingPointLiteral()) != null) 622 { 623 litType = BigDecimal .class; 624 litValue = fLiteral; 625 } 626 else if ((iLiteral = p.parseIntegerLiteral()) != null) 627 { 628 litType = Long .class; 629 litValue = new Long (iLiteral.longValue()); 630 } 631 else if ((cLiteral = p.parseCharacterLiteral()) != null) 632 { 633 litType = Character .class; 634 litValue = cLiteral; 635 } 636 else if ((bLiteral = p.parseBooleanLiteral()) != null) 637 { 638 litType = Boolean .class; 639 litValue = bLiteral; 640 } 641 else if (p.parseNullLiteral()) 642 return new NullLiteral(qs); 643 else 644 return null; 645 646 Mapping m = dba.getMapping(litType); 647 return m.newSQLLiteral(qs, litValue); 648 } 649 650 651 private SQLExpression compileIdentifier() 652 { 653 SQLExpression expr; 654 String id = p.parseIdentifier(); 655 656 if (id == null) 657 throw new ExpressionSyntaxException("Identifier expected"); 658 659 if (parameterNames.contains(id)) 660 { 661 Mapping m = dba.getMapping((Class )parameterTypesByName.get(id)); 662 663 if (!parameters.containsKey(id)) 664 throw new JDOUserException("Required parameter " + id + " not provided"); 665 666 Object parameterValue = parameters.get(id); 667 668 if (parameterValue == null) 669 expr = new NullLiteral(qs); 670 else 671 expr = m.newSQLLiteral(qs, parameterValue); 672 } 673 else if (variableNames.contains(id)) 674 { 675 expr = (SQLExpression)expressionsByVariableName.get(id); 676 677 if (expr == null) 678 expr = new UnboundVariable(qs, id, (Class )variableTypesByName.get(id), this); 679 } 680 else 681 expr = qs.getDefaultTableExpression().newFieldExpression(id); 682 683 return expr; 684 } 685 686 687 private class ExpressionSyntaxException extends JDOUserException 688 { 689 public ExpressionSyntaxException(String msg) 690 { 691 super(msg + " at character " + (p.getIndex() + 1) + " in \"" + p.getInput() + '"'); 692 } 693 } 694 } 695 } 696 | Popular Tags |