1 2 12 package com.versant.core.jdbc.fetch; 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 41 public class SqlBuffer { 42 43 private static final char[] COUNT_STAR_PRE = "COUNT(".toCharArray(); 44 private static final char[] COUNT_STAR_POST = ")".toCharArray(); 45 private static final char[] COUNT_STAR_PRE_DISTINCT = "COUNT(DISTINCT(".toCharArray(); 46 private static final char[] COUNT_STAR_POST_DISTINCT = "))".toCharArray(); 47 private static final char[] IS_NULL = "IS NULL".toCharArray(); 48 private static final char[] IS_NOT_NULL = "IS NOT NULL".toCharArray(); 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 SqlBuffer() { 140 sqlbuf = new CharBuf(256); 141 } 142 143 public SqlBuffer getClone() { 144 SqlBuffer clone = new SqlBuffer(); 145 clone.sqlbuf = new CharBuf(sqlbuf); 146 clone.distinct = distinct; 147 clone.selectListStartIndex = selectListStartIndex; 148 clone.selectListLength = selectListLength; 149 clone.orderByStartIndex = orderByStartIndex; 150 clone.orderByLength = orderByLength; 151 clone.firstTableOrAlias = firstTableOrAlias; 152 clone.sql = sql; 153 clone.sqlForUpdate = sqlForUpdate; 154 clone.sqlbufNotForUpdateSize = sqlbufNotForUpdateSize; 155 clone.sqlForCount = sqlForCount; 156 clone.originalSelectList = originalSelectList; 157 clone.originalOrderByClause = originalOrderByClause; 158 clone.paramList = (paramList == null ? null : paramList.getClone()); 159 if (isNullChars != null) { 160 clone.isNullChars = new char[isNullChars.length]; 161 for (int i = 0; i < isNullChars.length; i++) { 162 clone.isNullChars[i] = isNullChars[i]; 163 } 164 } 165 if (isNotNullChars != null) { 166 clone.isNotNullChars = new char[isNotNullChars.length]; 167 for (int i = 0; i < isNotNullChars.length; i++) { 168 clone.isNotNullChars[i] = isNotNullChars[i]; 169 } 170 } 171 return clone; 172 } 173 174 public boolean isAggregate() { 175 return aggregate; 176 } 177 178 public void setAggregate(boolean aggregate) { 179 this.aggregate = aggregate; 180 } 181 182 190 public void setSelectListRange(boolean distinct, int start, int firstColEnd, 191 int end) { 192 this.distinct = distinct; 193 if (distinct) { 194 selectListStartIndex = start - 9; } else { 196 selectListStartIndex = start; 197 } 198 selectFirstColStart = start; 199 selectListLength = end - selectListStartIndex; 200 selectFirstColLength = firstColEnd - selectFirstColStart; 201 } 202 203 207 public void setOrderByRange(int start, int end) { 208 orderByStartIndex = start; 209 orderByLength = end - start; 210 } 211 212 public CharBuf getSqlbuf() { 213 return sqlbuf; 214 } 215 216 public boolean isDistinct() { 217 return distinct; 218 } 219 220 public String getFirstTableOrAlias() { 221 return firstTableOrAlias; 222 } 223 224 public void setFirstTableOrAlias(String firstTableOrAlias) { 225 this.firstTableOrAlias = firstTableOrAlias; 226 } 227 228 public boolean isSqlForUpdate() { 229 return sqlForUpdate; 230 } 231 232 public int getSqlbufNotForUpdateSize() { 233 return sqlbufNotForUpdateSize; 234 } 235 236 public boolean isSqlForCount() { 237 return sqlForCount; 238 } 239 240 public char[] getOriginalSelectList() { 241 return originalSelectList; 242 } 243 244 public char[] getOriginalOrderByClause() { 245 return originalOrderByClause; 246 } 247 248 public Param getParamList() { 249 return paramList; 250 } 251 252 public void setParamList(Param paramList) { 253 this.paramList = paramList; 254 if (paramList != null) analyzeCharSpans(); 255 } 256 257 private void analyzeCharSpans() { 258 int max = IS_NOT_NULL.length; 259 for (Param p = paramList; p != null; p = p.next) { 260 for (CharSpan s = p.charSpanList; s != null; s = s.next) { 261 if (s.type == CharSpan.TYPE_REMOVE) continue; 262 int len = s.lastCharIndex - s.firstCharIndex; 263 if (len > max) max = len; 264 } 265 } 266 if (max > 0) { 267 isNullChars = new char[max]; 268 copyAndPad(IS_NULL, isNullChars, max); 269 isNotNullChars = new char[max]; 270 copyAndPad(IS_NOT_NULL, isNotNullChars, max); 271 } 272 } 273 274 277 public void createSpace(CharBuf charBuf, int index, int amount) { 278 sql = null; 279 for (SqlBuffer.Param p = paramList; p != null; p = p.next) { 280 if (p.firstCharIndex > index) { 281 p.firstCharIndex += amount; 283 for (CharSpan cs = p.charSpanList; cs != null; cs = cs.next) { 284 cs.firstCharIndex += amount; 285 cs.lastCharIndex += amount; 286 } 287 } 288 } 289 290 if (orderByStartIndex > index) { 291 orderByStartIndex += amount; 292 } 293 } 294 295 298 public void removeSpace(CharBuf charBuf, int index, int amount) { 299 sql = null; 300 for (SqlBuffer.Param p = paramList; p != null; p = p.next) { 301 if (p.firstCharIndex > index) { 302 p.firstCharIndex -= amount; 304 for (CharSpan cs = p.charSpanList; cs != null; cs = cs.next) { 305 cs.firstCharIndex -= amount; 306 cs.lastCharIndex -= amount; 307 } 308 } 309 } 310 311 if (orderByStartIndex > index) { 312 orderByStartIndex -= amount; 313 } 314 } 315 316 325 public synchronized String getSql(SqlDriver driver, Object [] params, 326 boolean forUpdate, boolean forCount, long fromIncl, long toExcl) { 327 boolean changed = sql == null; 328 if (params != null) { 329 int paramIndex = 0; 330 for (SqlBuffer.Param p = paramList; p != null; p = p.next, paramIndex++) { 331 if (params[p.declaredParamIndex] instanceof Collection ) { 332 Collection col = (Collection ) params[p.declaredParamIndex]; 333 int n = col.size(); 334 if (n == 0) { 335 throw BindingSupportImpl.getInstance().invalidOperation( 336 "The supplied collection param at index " 337 + paramIndex + " may not be empty"); 338 } 339 if (p.inListParamCount == 0) { 340 final char[] charsToInsert = driver.getSqlParamStringChars(p.jdbcType); 342 int toInsert = (n == 1 ? charsToInsert.length : (n * charsToInsert.length) + (n - 1)); 343 char[] chars = new char[toInsert]; 344 345 int offset = 0; 346 for (int i = 0; i < n; i++) { 347 if (offset > 0) { 348 chars[offset++] = ','; 349 } 350 for (int j = 0; j < charsToInsert.length; j++) { 351 chars[j + offset] = charsToInsert[j]; 352 } 353 offset += charsToInsert.length; 354 } 355 createSpace(sqlbuf, p.firstCharIndex, toInsert); 356 sqlbuf.insert(p.firstCharIndex, chars); 357 p.charLength = chars.length; 358 p.inListParamCount = n; 359 changed = true; 360 } else if (p.inListParamCount < n) { 361 int insertPoint = p.charLength + p.firstCharIndex; 363 int paramsToInsert = n - p.inListParamCount; 365 final char[] charStamp = driver.getSqlParamStringChars(p.jdbcType); 366 int charsToInsert = (charStamp.length * paramsToInsert) + paramsToInsert; 367 char[] chars = new char[charsToInsert]; 368 int offset = 0; 369 for (int i = 0; i < paramsToInsert; i++) { 370 chars[offset++] = ','; 371 for (int j = 0; j < charStamp.length; j++) { 372 chars[offset++] = charStamp[j]; 373 } 374 } 375 if (Debug.DEBUG) { 376 if (offset != chars.length) { 377 throw BindingSupportImpl.getInstance().internal(""); 378 } 379 } 380 createSpace(sqlbuf, insertPoint, charsToInsert); 381 sqlbuf.insert(insertPoint, chars); 382 p.charLength += chars.length; 383 p.inListParamCount = n; 384 changed = true; 385 } else if (p.inListParamCount > n) { 386 changed = true; 388 int removeStart = p.firstCharIndex; 389 int paramToRemove = p.inListParamCount - n; 390 391 int charsToRemove = driver.getSqlParamStringChars(p.jdbcType).length * paramToRemove + paramToRemove; 392 int removeTo = charsToRemove + removeStart; 393 sqlbuf.remove(removeStart, removeTo); 394 removeSpace(sqlbuf, p.firstCharIndex, removeTo - removeStart); 395 396 p.charLength -= (removeTo - removeStart); 397 p.inListParamCount = n; 398 changed = true; 399 } 400 401 } 402 } 403 } 404 405 if (params != null) { 406 for (SqlBuffer.Param p = paramList; p != null; p = p.next) { 407 if (p.update(this, params[p.declaredParamIndex] == null)) { 408 changed = true; 409 } 410 } 411 } 412 if (forUpdate != sqlForUpdate 413 && (!distinct || driver.isSelectForUpdateWithDistinctOk()) 414 && (!aggregate || driver.isSelectForUpdateWithAggregateOk())) { 415 char[] a = driver.getSelectForUpdate(); 416 if (a != null) { 417 if (forUpdate) { 418 sqlbufNotForUpdateSize = sqlbuf.size(); 419 sqlbuf.append(a); 420 if (driver.isSelectForUpdateAppendTable()) { 421 sqlbuf.append(firstTableOrAlias); 422 } 423 } else { 424 sqlbuf.setSize(sqlbufNotForUpdateSize); 425 } 426 sqlForUpdate = forUpdate; 427 changed = true; 428 } 429 } 430 if (forCount != sqlForCount) { 431 if (forCount) { 432 if (originalSelectList == null) { 433 originalSelectList = sqlbuf.toArray(selectListStartIndex, 434 selectListLength); 435 originalSelectListFirstColumn = sqlbuf.toArray( 436 selectFirstColStart, selectFirstColLength); 437 } 438 int start; 439 if (distinct) { 440 sqlbuf.replace(selectListStartIndex, COUNT_STAR_PRE_DISTINCT); 441 start = selectListStartIndex + COUNT_STAR_PRE_DISTINCT.length; 442 } else { 443 sqlbuf.replace(selectListStartIndex, COUNT_STAR_PRE); 444 start = selectListStartIndex + COUNT_STAR_PRE.length; 445 } 446 sqlbuf.replace(start, originalSelectListFirstColumn); 447 start += originalSelectListFirstColumn.length; 448 if (distinct) { 449 sqlbuf.replace(start, COUNT_STAR_POST_DISTINCT); 450 start += COUNT_STAR_POST_DISTINCT.length; 451 } else { 452 sqlbuf.replace(start, COUNT_STAR_POST); 453 start += COUNT_STAR_POST.length; 454 } 455 int n = (selectListStartIndex + selectListLength) - start; 456 if (n > 0) sqlbuf.replace(start, start + n, ' '); 457 if (orderByStartIndex > 0) { 458 if (originalOrderByClause == null) { 459 originalOrderByClause = sqlbuf.toArray(orderByStartIndex, 460 orderByLength); 461 } 462 sqlbuf.replace(orderByStartIndex, 463 orderByStartIndex + orderByLength, ' '); 464 } 465 } else { 466 sqlbuf.replace(selectListStartIndex, originalSelectList); 467 if (orderByStartIndex > 0) { 468 sqlbuf.replace(orderByStartIndex, originalOrderByClause); 469 } 470 } 471 sqlForCount = forCount; 472 changed = true; 473 } 474 475 if (changed) { 476 sql = sqlbuf.toString(); 477 } 478 return sql; 479 } 480 481 485 public void setParamsOnPS(ModelMetaData jmd, SqlDriver driver, 486 PreparedStatement ps, Object [] params, String sql) { 487 if (params == null) return; 488 int pos = 1; 489 SqlBuffer.Param p = paramList; 490 Object value = null; 491 try { 492 for (; p != null; p = p.next) { 493 value = params[p.declaredParamIndex]; 494 switch (p.mod) { 495 case Param.MOD_NONE: 496 break; 497 case Param.MOD_APPEND_PERCENT: 498 if (value != null) value = value + "%"; 499 break; 500 case Param.MOD_PREPEND_PERCENT: 501 if (value != null) value = "%" + value; 502 break; 503 default: 504 throw BindingSupportImpl.getInstance().internal("Invalid mod: " 505 + p.mod); 506 } 507 if (value == null && p.requiresUpdate()) continue; 508 int pci = p.classIndex; 509 if (pci >= 0) { 510 ClassMetaData pcmd = jmd.classes[pci]; 511 int pfno = p.fieldNo; 512 if (pfno >= 0) { 513 JdbcField f = ((JdbcClass)pcmd.storeClass).stateFields[pfno]; 514 if (value instanceof Collection ) { 515 Collection col = (Collection ) value; 516 for (Iterator iterator = col.iterator(); iterator.hasNext();) { 517 Object o = iterator.next(); 518 if (o instanceof OID) { 519 pos = ((JdbcOID)o).setParams(ps, pos); 520 } else { 521 pos = f.setQueryParam(ps, pos, o); 522 } 523 } 524 } else { 525 pos = f.setQueryParam(ps, pos, value); 526 } 527 } else { if (value != null) { 529 pos = ((JdbcOID)value).setParams(ps, pos); 530 } else { 531 JdbcColumn[] pkcols = ((JdbcClass)pcmd.storeClass).table.pkSimpleCols; 532 int nc = pkcols.length; 533 for (int i = 0; i < nc; i++) { 534 ps.setNull(pos++, pkcols[i].jdbcType); 535 } 536 } 537 } 538 } else { 539 if (p.col != null) { 540 p.col.set(ps, pos++, value); 541 } else { 542 int javaTypeCode = p.javaTypeCode; 543 if (javaTypeCode == 0 && value != null) { 544 javaTypeCode = MDStaticUtils.toTypeCode(value.getClass()); 545 } 546 JdbcUtils.set(ps, pos++, value, javaTypeCode, p.jdbcType); 547 } 548 } 549 } 550 } catch (Exception e) { 551 throw driver.mapException(e, "Error setting query parameter " + 552 p.getIdentifier() + " = '" + Utils.toString(value) + 553 "' at PreparedStatement index " + pos + " in\n" + 554 JdbcUtils.getPreparedStatementInfo(sql, ps) + "\n" + 555 JdbcUtils.toString(e), false); 556 } 557 } 558 559 private static void copyAndPad(char[] src, char[] dest, int len) { 560 int n = src.length; 561 System.arraycopy(src, 0, dest, 0, n); 562 for (; n < len;) dest[n++] = ' '; 563 } 564 565 public char[] getNullChars() { 566 return isNullChars; 567 } 568 569 public void setNullChars(char[] nullChars) { 570 isNullChars = nullChars; 571 } 572 573 public char[] getNotNullChars() { 574 return isNotNullChars; 575 } 576 577 public void setNotNullChars(char[] notNullChars) { 578 isNotNullChars = notNullChars; 579 } 580 581 586 public final static class Param implements Serializable { 587 588 public static final int MOD_NONE = 0; 589 public static final int MOD_PREPEND_PERCENT = 1; 590 public static final int MOD_APPEND_PERCENT = 2; 591 592 595 private String identifier; 596 599 public Param next; 600 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(SqlBuffer 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 return clone; 705 } 706 } 707 708 713 public final static class CharSpan implements Serializable { 714 715 public static final int TYPE_NULL = 1; 716 public static final int TYPE_NOT_NULL = 2; 717 public static final int TYPE_REMOVE = 3; 718 719 722 public int type; 723 726 public int firstCharIndex; 727 730 public int lastCharIndex; 731 734 public CharSpan next; 735 736 740 private boolean paramIsNull; 741 746 private char[] originalSql; 747 748 public CharSpan getClone() { 749 CharSpan clone = new CharSpan(); 750 clone.type = type; 751 clone.firstCharIndex = firstCharIndex; 752 clone.lastCharIndex = lastCharIndex; 753 clone.next = (next == null ? null : next.getClone()); 754 clone.paramIsNull = paramIsNull; 755 if (originalSql != null) { 756 clone.originalSql = new char[originalSql.length]; 757 for (int i = 0; i < originalSql.length; i++) { 758 clone.originalSql[i] = originalSql[i]; 759 } 760 } 761 return clone; 762 } 763 764 769 public boolean update(SqlBuffer q, boolean newParamIsNull) { 770 if (newParamIsNull == paramIsNull) return false; 771 CharBuf sql = q.sqlbuf; 772 if (newParamIsNull) { 773 if (originalSql == null) { 774 originalSql = sql.toArray(firstCharIndex, 775 lastCharIndex - firstCharIndex); 776 } 777 if (Debug.DEBUG) { 778 System.out.println("*** CharSpan.update replacing '" + 779 new String (originalSql) + "' " + originalSql.length + 780 " " + q.isNullChars.length); 781 } 782 switch (type) { 783 case TYPE_NULL: 784 sql.replace(firstCharIndex, q.isNullChars); 785 break; 786 case TYPE_NOT_NULL: 787 sql.replace(firstCharIndex, q.isNotNullChars); 788 break; 789 case TYPE_REMOVE: 790 sql.replace(firstCharIndex, lastCharIndex, ' '); 791 break; 792 default: 793 throw BindingSupportImpl.getInstance().internal( 794 "Unknown CharSpan type: " + type); 795 } 796 } else { 797 sql.replace(firstCharIndex, originalSql); 798 } 799 paramIsNull = newParamIsNull; 800 return true; 801 } 802 } 803 804 } 805 | Popular Tags |