1 2 12 package com.versant.core.jdbc.query; 13 14 import com.versant.core.util.CharBuf; 15 import com.versant.core.common.Debug; 16 import com.versant.core.common.OID; 17 import com.versant.core.common.Utils; 18 import com.versant.core.metadata.ModelMetaData; 19 import com.versant.core.metadata.ClassMetaData; 20 import com.versant.core.metadata.MDStaticUtils; 21 import com.versant.core.jdbc.sql.SqlDriver; 22 import com.versant.core.jdbc.metadata.JdbcColumn; 23 import com.versant.core.jdbc.metadata.JdbcField; 24 import com.versant.core.jdbc.metadata.JdbcClass; 25 import com.versant.core.jdbc.JdbcUtils; 26 import com.versant.core.jdbc.JdbcOID; 27 28 import java.io.Serializable ; 29 import java.sql.PreparedStatement ; 30 import java.sql.SQLException ; 31 import java.util.Collection ; 32 import java.util.Iterator ; 33 34 import com.versant.core.common.BindingSupportImpl; 35 36 39 public class SqlStruct { 40 41 private static final char[] COUNT_STAR_PRE = "COUNT(".toCharArray(); 42 private static final char[] COUNT_STAR_POST = ")".toCharArray(); 43 private static final char[] COUNT_STAR_PRE_DISTINCT = "COUNT(DISTINCT(".toCharArray(); 44 private static final char[] COUNT_STAR_POST_DISTINCT = "))".toCharArray(); 45 private static final char[] IS_NULL = "is null".toCharArray(); 46 private static final char[] IS_NOT_NULL = "is not null".toCharArray(); 47 48 public String jdoqlFilter; 49 50 55 private CharBuf sqlbuf; 56 59 private boolean distinct; 60 63 private int selectListStartIndex; 64 67 private int selectListLength; 68 71 private int selectFirstColStart; 72 75 private int selectFirstColLength; 76 79 private int orderByStartIndex; 80 83 private int orderByLength; 84 89 private String firstTableOrAlias; 90 93 private transient String sql; 94 97 private boolean sqlForUpdate; 98 102 private int sqlbufNotForUpdateSize; 103 106 private boolean sqlForCount; 107 110 private char[] originalSelectList; 111 115 private char[] originalSelectListFirstColumn; 116 119 private char[] originalOrderByClause; 120 125 private Param paramList; 126 129 private char[] isNullChars; 130 133 private char[] isNotNullChars; 134 137 private boolean aggregate; 138 139 public SqlStruct() { 140 sqlbuf = new CharBuf(256); 141 } 142 143 public synchronized SqlStruct getClone() { 144 SqlStruct clone = new SqlStruct(); 145 clone.jdoqlFilter = jdoqlFilter; 146 clone.sqlbuf = new CharBuf(sqlbuf); 147 clone.distinct = distinct; 148 clone.selectListStartIndex = selectListStartIndex; 149 clone.selectListLength = selectListLength; 150 clone.orderByStartIndex = orderByStartIndex; 151 clone.orderByLength = orderByLength; 152 clone.firstTableOrAlias = firstTableOrAlias; 153 clone.sql = sql; 154 clone.sqlForUpdate = sqlForUpdate; 155 clone.sqlbufNotForUpdateSize = sqlbufNotForUpdateSize; 156 clone.sqlForCount = sqlForCount; 157 clone.originalSelectList = originalSelectList; 158 clone.originalOrderByClause = originalOrderByClause; 159 clone.paramList = (paramList == null ? null : paramList.getClone()); 160 161 if (isNullChars != null) { 162 clone.isNullChars = new char[isNullChars.length]; 163 for (int i = 0; i < isNullChars.length; i++) { 164 clone.isNullChars[i] = isNullChars[i]; 165 } 166 } 167 168 if (isNotNullChars != null) { 169 clone.isNotNullChars = new char[isNotNullChars.length]; 170 for (int i = 0; i < isNotNullChars.length; i++) { 171 clone.isNotNullChars[i] = isNotNullChars[i]; 172 } 173 } 174 return clone; 175 } 176 177 public boolean isAggregate() { 178 return aggregate; 179 } 180 181 public void setAggregate(boolean aggregate) { 182 this.aggregate = aggregate; 183 } 184 185 193 public void setSelectListRange(boolean distinct, int start, int firstColEnd, 194 int end) { 195 this.distinct = distinct; 196 if (distinct) { 197 selectListStartIndex = start - 9; } else { 199 selectListStartIndex = start; 200 } 201 selectFirstColStart = start; 202 selectListLength = end - selectListStartIndex; 203 selectFirstColLength = firstColEnd - selectFirstColStart; 204 } 205 206 210 public void setOrderByRange(int start, int end) { 211 orderByStartIndex = start; 212 orderByLength = end - start; 213 } 214 215 public CharBuf getSqlbuf() { 216 return sqlbuf; 217 } 218 219 public boolean isDistinct() { 220 return distinct; 221 } 222 223 public String getFirstTableOrAlias() { 224 return firstTableOrAlias; 225 } 226 227 public void setFirstTableOrAlias(String firstTableOrAlias) { 228 this.firstTableOrAlias = firstTableOrAlias; 229 } 230 231 public String getSql() { 232 if (sql == null) sql = sqlbuf.toString(); 233 return sql; 234 } 235 236 public boolean isSqlForUpdate() { 237 return sqlForUpdate; 238 } 239 240 public int getSqlbufNotForUpdateSize() { 241 return sqlbufNotForUpdateSize; 242 } 243 244 public boolean isSqlForCount() { 245 return sqlForCount; 246 } 247 248 public char[] getOriginalSelectList() { 249 return originalSelectList; 250 } 251 252 public char[] getOriginalOrderByClause() { 253 return originalOrderByClause; 254 } 255 256 public Param getParamList() { 257 return paramList; 258 } 259 260 public void setParamList(Param paramList) { 261 this.paramList = paramList; 262 if (paramList != null) analyzeCharSpans(); 263 } 264 265 private void analyzeCharSpans() { 266 int max = IS_NOT_NULL.length; 267 for (Param p = paramList; p != null; p = p.next) { 268 for (CharSpan s = p.charSpanList; s != null; s = s.next) { 269 if (s.type == CharSpan.TYPE_REMOVE) continue; 270 int len = s.lastCharIndex - s.firstCharIndex; 271 if (len > max) max = len; 272 } 273 } 274 if (max > 0) { 275 isNullChars = new char[max]; 276 copyAndPad(IS_NULL, isNullChars, max); 277 isNotNullChars = new char[max]; 278 copyAndPad(IS_NOT_NULL, isNotNullChars, max); 279 } 280 } 281 282 285 public void createSpace(CharBuf charBuf, int index, int amount) { 286 sql = null; 287 for (SqlStruct.Param p = paramList; p != null; p = p.next) { 288 if (p.firstCharIndex > index) { 289 p.firstCharIndex += amount; 291 for (CharSpan cs = p.charSpanList; cs != null; cs = cs.next) { 292 cs.firstCharIndex += amount; 293 cs.lastCharIndex += amount; 294 } 295 } 296 } 297 298 if (orderByStartIndex > index) { 299 orderByStartIndex += amount; 300 } 301 } 302 303 306 public void removeSpace(CharBuf charBuf, int index, int amount) { 307 sql = null; 308 for (SqlStruct.Param p = paramList; p != null; p = p.next) { 309 if (p.firstCharIndex > index) { 310 p.firstCharIndex -= amount; 312 for (CharSpan cs = p.charSpanList; cs != null; cs = cs.next) { 313 cs.firstCharIndex -= amount; 314 cs.lastCharIndex -= amount; 315 } 316 } 317 } 318 319 if (orderByStartIndex > index) { 320 orderByStartIndex -= amount; 321 } 322 } 323 324 329 public synchronized void updateSql(SqlDriver driver, Object [] params, boolean forUpdate, 330 boolean forCount) { 331 boolean changed = false; 332 if (params != null) { 333 int paramIndex = 0; 334 for (SqlStruct.Param p = paramList; p != null; p = p.next, paramIndex++) { 335 if (params[p.declaredParamIndex] instanceof Collection ) { 336 Collection col = (Collection ) params[p.declaredParamIndex]; 337 int n = col.size(); 338 if (n == 0) { 339 throw BindingSupportImpl.getInstance().invalidOperation( 340 "The supplied collection param at index " 341 + paramIndex + " may not be empty"); 342 } 343 if (p.inListParamCount == 0) { 344 final char[] charsToInsert = driver.getSqlParamStringChars(p.jdbcType); 346 int toInsert = (n == 1 ? charsToInsert.length : (n * charsToInsert.length) + (n - 1)); 347 char[] chars = new char[toInsert]; 348 349 int offset = 0; 350 for (int i = 0; i < n; i++) { 351 if (offset > 0) { 352 chars[offset++] = ','; 353 } 354 for (int j = 0; j < charsToInsert.length; j++) { 355 chars[j + offset] = charsToInsert[j]; 356 } 357 offset += charsToInsert.length; 358 } 359 createSpace(sqlbuf, p.firstCharIndex, toInsert); 360 sqlbuf.insert(p.firstCharIndex, chars); 361 p.charLength = chars.length; 362 p.inListParamCount = n; 363 changed = true; 364 } else if (p.inListParamCount < n) { 365 int insertPoint = p.charLength + p.firstCharIndex; 367 int paramsToInsert = n - p.inListParamCount; 369 final char[] charStamp = driver.getSqlParamStringChars(p.jdbcType); 370 int charsToInsert = (charStamp.length * paramsToInsert) + paramsToInsert; 371 char[] chars = new char[charsToInsert]; 372 int offset = 0; 373 for (int i = 0; i < paramsToInsert; i++) { 374 chars[offset++] = ','; 375 for (int j = 0; j < charStamp.length; j++) { 376 chars[offset++] = charStamp[j]; 377 } 378 } 379 if (Debug.DEBUG) { 380 if (offset != chars.length) { 381 throw BindingSupportImpl.getInstance().internal(""); 382 } 383 } 384 createSpace(sqlbuf, insertPoint, charsToInsert); 385 sqlbuf.insert(insertPoint, chars); 386 p.charLength += chars.length; 387 p.inListParamCount = n; 388 changed = true; 389 } else if (p.inListParamCount > n) { 390 changed = true; 392 int removeStart = p.firstCharIndex; 393 int paramToRemove = p.inListParamCount - n; 394 395 int charsToRemove = driver.getSqlParamStringChars(p.jdbcType).length * paramToRemove + paramToRemove; 396 int removeTo = charsToRemove + removeStart; 397 sqlbuf.remove(removeStart, removeTo); 398 removeSpace(sqlbuf, p.firstCharIndex, removeTo - removeStart); 399 400 p.charLength -= (removeTo - removeStart); 401 p.inListParamCount = n; 402 changed = true; 403 } 404 405 } 406 } 407 } 408 409 if (params != null) { 410 for (SqlStruct.Param p = paramList; p != null; p = p.next) { 411 if (p.update(this, params[p.declaredParamIndex] == null)) { 412 changed = true; 413 } 414 } 415 } 416 if (forUpdate != sqlForUpdate 417 && (!distinct || driver.isSelectForUpdateWithDistinctOk()) 418 && (!aggregate || driver.isSelectForUpdateWithAggregateOk())) { 419 char[] a = driver.getSelectForUpdate(); 420 if (a != null) { 421 if (forUpdate) { 422 sqlbufNotForUpdateSize = sqlbuf.size(); 423 sqlbuf.append(a); 424 if (driver.isSelectForUpdateAppendTable()) { 425 sqlbuf.append(firstTableOrAlias); 426 } 427 } else { 428 sqlbuf.setSize(sqlbufNotForUpdateSize); 429 } 430 sqlForUpdate = forUpdate; 431 changed = true; 432 } 433 } 434 if (forCount != sqlForCount) { 435 if (forCount) { 436 if (originalSelectList == null) { 437 originalSelectList = sqlbuf.toArray(selectListStartIndex, 438 selectListLength); 439 originalSelectListFirstColumn = sqlbuf.toArray( 440 selectFirstColStart, selectFirstColLength); 441 } 442 int start; 443 if (distinct) { 444 sqlbuf.replace(selectListStartIndex, COUNT_STAR_PRE_DISTINCT); 445 start = selectListStartIndex + COUNT_STAR_PRE_DISTINCT.length; 446 } else { 447 sqlbuf.replace(selectListStartIndex, COUNT_STAR_PRE); 448 start = selectListStartIndex + COUNT_STAR_PRE.length; 449 } 450 sqlbuf.replace(start, originalSelectListFirstColumn); 451 start += originalSelectListFirstColumn.length; 452 if (distinct) { 453 sqlbuf.replace(start, COUNT_STAR_POST_DISTINCT); 454 start += COUNT_STAR_POST_DISTINCT.length; 455 } else { 456 sqlbuf.replace(start, COUNT_STAR_POST); 457 start += COUNT_STAR_POST.length; 458 } 459 int n = (selectListStartIndex + selectListLength) - start; 460 if (n > 0) sqlbuf.replace(start, start + n, ' '); 461 if (orderByStartIndex > 0) { 462 if (originalOrderByClause == null) { 463 originalOrderByClause = sqlbuf.toArray(orderByStartIndex, 464 orderByLength); 465 } 466 sqlbuf.replace(orderByStartIndex, 467 orderByStartIndex + orderByLength, ' '); 468 } 469 } else { 470 sqlbuf.replace(selectListStartIndex, originalSelectList); 471 if (orderByStartIndex > 0) { 472 sqlbuf.replace(orderByStartIndex, originalOrderByClause); 473 } 474 } 475 sqlForCount = forCount; 476 changed = true; 477 } 478 479 if (changed) sql = null; 480 } 481 482 486 public synchronized void setParamsOnPS(ModelMetaData jmd, SqlDriver driver, 487 PreparedStatement ps, Object [] params, String sql) throws SQLException { 488 if (params == null) return; 489 int pos = 1; 490 SqlStruct.Param p = paramList; 491 Object value = null; 492 try { 493 for (; p != null; p = p.next) { 494 value = params[p.declaredParamIndex]; 495 switch (p.mod) { 496 case Param.MOD_NONE: 497 break; 498 case Param.MOD_APPEND_PERCENT: 499 if (value != null) value = value + "%"; 500 break; 501 case Param.MOD_PREPEND_PERCENT: 502 if (value != null) value = "%" + value; 503 break; 504 default: 505 throw BindingSupportImpl.getInstance().internal("Invalid mod: " 506 + p.mod); 507 } 508 if (value == null && p.requiresUpdate()) continue; 509 int pci = p.classIndex; 510 if (pci >= 0) { 511 ClassMetaData pcmd = jmd.classes[pci]; 512 int pfno = p.fieldNo; 513 if (pfno >= 0) { 514 JdbcField f = ((JdbcClass)pcmd.storeClass).stateFields[pfno]; 515 if (value instanceof Collection ) { 516 Collection col = (Collection ) value; 517 for (Iterator iterator = col.iterator(); iterator.hasNext();) { 518 Object o = iterator.next(); 519 if (o instanceof OID) { 520 pos = ((JdbcOID)o).setParams(ps, pos); 521 } else { 522 pos = f.setQueryParam(ps, pos, o); 523 } 524 } 525 } else { 526 pos = f.setQueryParam(ps, pos, value); 527 } 528 } else { if (value != null) { 530 pos = ((JdbcOID)value).setParams(ps, pos); 531 } else { 532 JdbcColumn[] pkcols = ((JdbcClass)pcmd.storeClass).table.pkSimpleCols; 533 int nc = pkcols.length; 534 for (int i = 0; i < nc; i++) { 535 ps.setNull(pos++, pkcols[i].jdbcType); 536 } 537 } 538 } 539 } else { 540 if (p.col != null) { 541 p.col.set(ps, pos++, value); 542 } else { 543 int javaTypeCode = p.javaTypeCode; 544 if (javaTypeCode == 0 && value != null) { 545 javaTypeCode = MDStaticUtils.toTypeCode(value.getClass()); 546 } 547 JdbcUtils.set(ps, pos++, value, javaTypeCode, p.jdbcType); 548 } 549 } 550 } 551 } catch (Exception e) { 552 throw driver.mapException(e, "Error setting query parameter " + 553 p.getIdentifier() + " = '" + Utils.toString(value) + 554 "' at PreparedStatement index " + pos + " in\n" + 555 JdbcUtils.getPreparedStatementInfo(sql, ps) + "\n" + 556 JdbcUtils.toString(e), false); 557 } 558 } 559 560 private static void copyAndPad(char[] src, char[] dest, int len) { 561 int n = src.length; 562 System.arraycopy(src, 0, dest, 0, n); 563 for (; n < len;) dest[n++] = ' '; 564 } 565 566 public char[] getNullChars() { 567 return isNullChars; 568 } 569 570 public void setNullChars(char[] nullChars) { 571 isNullChars = nullChars; 572 } 573 574 public char[] getNotNullChars() { 575 return isNotNullChars; 576 } 577 578 public void setNotNullChars(char[] notNullChars) { 579 isNotNullChars = notNullChars; 580 } 581 582 587 public final static class Param implements Serializable { 588 589 public static final int MOD_NONE = 0; 590 public static final int MOD_PREPEND_PERCENT = 1; 591 public static final int MOD_APPEND_PERCENT = 2; 592 593 596 private String identifier; 597 600 public Param next; 601 605 public int declaredParamIndex; 606 611 public transient int firstCharIndex; 612 616 public transient int charLength; 617 621 public transient int inListParamCount; 622 627 public CharSpan charSpanList; 628 633 public int classIndex; 634 640 public int fieldNo; 641 645 public int javaTypeCode; 646 651 public int jdbcType; 652 658 public int mod; 659 660 public transient JdbcColumn col; 661 662 public Param(String identifier) { 663 this.identifier = identifier; 664 } 665 666 public String getIdentifier() { 667 return identifier; 668 } 669 670 676 public boolean update(SqlStruct q, boolean newParamIsNull) { 677 boolean ans = false; 678 for (CharSpan cs = charSpanList; cs != null; cs = cs.next) { 679 if (cs.update(q, newParamIsNull)) ans = true; 680 } 681 return ans; 682 } 683 684 688 public boolean requiresUpdate() { 689 return charSpanList != null; 690 } 691 692 public Param getClone() { 693 Param clone = new Param(identifier); 694 clone.next = (next == null ? null : next.getClone()); 695 clone.declaredParamIndex = declaredParamIndex; 696 clone.firstCharIndex = firstCharIndex; 697 clone.charSpanList = (charSpanList == null ? null : charSpanList.getClone()); 698 clone.classIndex = classIndex; 699 clone.fieldNo = fieldNo; 700 clone.javaTypeCode = javaTypeCode; 701 clone.jdbcType = jdbcType; 702 clone.mod = mod; 703 clone.col = col; 704 705 return clone; 706 } 707 } 708 709 714 public final static class CharSpan implements Serializable { 715 716 public static final int TYPE_NULL = 1; 717 public static final int TYPE_NOT_NULL = 2; 718 public static final int TYPE_REMOVE = 3; 719 720 723 public int type; 724 727 public int firstCharIndex; 728 731 public int lastCharIndex; 732 735 public CharSpan next; 736 737 741 private boolean paramIsNull; 742 747 private char[] originalSql; 748 749 public CharSpan getClone() { 750 CharSpan clone = new CharSpan(); 751 clone.type = type; 752 clone.firstCharIndex = firstCharIndex; 753 clone.lastCharIndex = lastCharIndex; 754 clone.next = (next == null ? null : next.getClone()); 755 clone.paramIsNull = paramIsNull; 756 if (originalSql != null) { 757 clone.originalSql = new char[originalSql.length]; 758 for (int i = 0; i < originalSql.length; i++) { 759 clone.originalSql[i] = originalSql[i]; 760 } 761 } 762 return clone; 763 } 764 765 770 public boolean update(SqlStruct q, boolean newParamIsNull) { 771 if (newParamIsNull == paramIsNull) return false; 772 CharBuf sql = q.sqlbuf; 773 if (newParamIsNull) { 774 if (originalSql == null) { 775 originalSql = sql.toArray(firstCharIndex, 776 lastCharIndex - firstCharIndex); 777 } 778 if (Debug.DEBUG) { 779 System.out.println("*** CharSpan.update replacing '" + 780 new String (originalSql) + "' " + originalSql.length + 781 " " + q.isNullChars.length); 782 } 783 switch (type) { 784 case TYPE_NULL: 785 sql.replace(firstCharIndex, q.isNullChars); 786 break; 787 case TYPE_NOT_NULL: 788 sql.replace(firstCharIndex, q.isNotNullChars); 789 break; 790 case TYPE_REMOVE: 791 sql.replace(firstCharIndex, lastCharIndex, ' '); 792 break; 793 default: 794 throw BindingSupportImpl.getInstance().internal( 795 "Unknown CharSpan type: " + type); 796 } 797 } else { 798 sql.replace(firstCharIndex, originalSql); 799 } 800 paramIsNull = newParamIsNull; 801 return true; 802 } 803 } 804 805 } 806 | Popular Tags |