1 24 25 package com.mckoi.database; 26 27 import java.lang.reflect.*; 28 import com.mckoi.database.global.BlobAccessor; 29 import com.mckoi.database.global.StringAccessor; 30 import com.mckoi.util.BigNumber; 31 import java.io.IOException ; 32 import java.io.InputStream ; 33 import java.util.StringTokenizer ; 34 import java.util.ArrayList ; 35 36 42 43 public class ProcedureManager { 44 45 48 private DatabaseConnection connection; 49 50 53 private DatabaseQueryContext context; 54 55 58 ProcedureManager(DatabaseConnection connection) { 59 this.connection = connection; 60 this.context = new DatabaseQueryContext(connection); 61 } 62 63 68 private Table findProcedureEntry(DataTable table, 69 ProcedureName procedure_name) { 70 71 Operator EQUALS = Operator.get("="); 72 73 Variable schemav = table.getResolvedVariable(0); 74 Variable namev = table.getResolvedVariable(1); 75 76 Table t = table.simpleSelect(context, namev, EQUALS, 77 new Expression(TObject.stringVal(procedure_name.getName()))); 78 t = t.exhaustiveSelect(context, Expression.simple( 79 schemav, EQUALS, TObject.stringVal(procedure_name.getSchema()))); 80 81 if (t.getRowCount() > 1) { 83 throw new RuntimeException ( 84 "Assert failed: multiple procedure names for " + procedure_name); 85 } 86 87 return t; 89 90 } 91 92 96 private static String procedureInfoString(ProcedureName name, 97 TType ret, TType[] params) { 98 StringBuffer buf = new StringBuffer (); 99 if (ret != null) { 100 buf.append(ret.asSQLString()); 101 buf.append(" "); 102 } 103 buf.append(name.getName()); 104 buf.append("("); 105 for (int i = 0; i < params.length; ++i) { 106 buf.append(params[i].asSQLString()); 107 if (i < params.length - 1) { 108 buf.append(", "); 109 } 110 } 111 buf.append(")"); 112 return new String (buf); 113 } 114 115 124 public static String [] parseJavaLocationString(final String str) { 125 int parenthese_delim = str.indexOf("("); 127 String class_method; 128 129 if (parenthese_delim != -1) { 130 class_method = str.substring(0, parenthese_delim); 132 int method_delim = class_method.lastIndexOf("."); 134 if (method_delim == -1) { 135 throw new StatementException( 136 "Incorrectly formatted Java method string: " + str); 137 } 138 String class_str = class_method.substring(0, method_delim); 139 String method_str = class_method.substring(method_delim + 1); 140 int end_parenthese_delim = str.lastIndexOf(")"); 142 if (end_parenthese_delim == -1) { 143 throw new StatementException( 144 "Incorrectly formatted Java method string: " + str); 145 } 146 String arg_list_str = 147 str.substring(parenthese_delim + 1, end_parenthese_delim); 148 ArrayList arg_list = new ArrayList (); 150 StringTokenizer tok = new StringTokenizer (arg_list_str, ","); 151 while (tok.hasMoreTokens()) { 152 String arg = tok.nextToken(); 153 arg_list.add(arg); 154 } 155 156 int sz = arg_list.size(); 158 String [] return_array = new String [2 + sz]; 159 return_array[0] = class_str; 160 return_array[1] = method_str; 161 for (int i = 0; i < sz; ++i) { 162 return_array[i + 2] = (String ) arg_list.get(i); 163 } 164 return return_array; 165 166 } 167 else { 168 return new String [] { str }; 170 } 171 172 } 173 174 177 public boolean procedureExists(ProcedureName procedure_name) { 178 179 DataTable table = connection.getTable(Database.SYS_FUNCTION); 180 return findProcedureEntry(table, procedure_name).getRowCount() == 1; 181 182 } 183 184 187 public boolean procedureExists(TableName procedure_name) { 188 return procedureExists(new ProcedureName(procedure_name)); 189 } 190 191 198 public void defineJavaProcedure(ProcedureName procedure_name, 199 String java_specification, 200 TType return_type, TType[] param_types, 201 String username) 202 throws DatabaseException { 203 204 TableName proc_table_name = 205 new TableName(procedure_name.getSchema(), procedure_name.getName()); 206 207 DatabaseConnection.checkAllowCreate(proc_table_name); 209 210 DataTable table = connection.getTable(Database.SYS_FUNCTION); 211 212 RowData row_data = new RowData(table); 214 row_data.setColumnDataFromObject(0, procedure_name.getSchema()); 215 row_data.setColumnDataFromObject(1, procedure_name.getName()); 216 row_data.setColumnDataFromObject(2, "Java-1"); 217 row_data.setColumnDataFromObject(3, java_specification); 218 if (return_type != null) { 219 row_data.setColumnDataFromObject(4, TType.asEncodedString(return_type)); 220 } 221 row_data.setColumnDataFromObject(5, TType.asEncodedString(param_types)); 222 row_data.setColumnDataFromObject(6, username); 223 224 Table t = findProcedureEntry(table, procedure_name); 226 227 if (t.getRowCount() == 1) { 229 table.delete(t); 230 } 231 232 table.add(row_data); 234 235 connection.databaseObjectCreated(proc_table_name); 237 238 } 239 240 244 public void deleteProcedure(ProcedureName procedure_name) 245 throws DatabaseException { 246 247 DataTable table = connection.getTable(Database.SYS_FUNCTION); 248 249 Table t = findProcedureEntry(table, procedure_name); 251 252 if (t.getRowCount() == 0) { 254 throw new StatementException("Procedure " + procedure_name + 255 " doesn't exist."); 256 } 257 258 table.delete(t); 259 260 connection.databaseObjectDropped( 262 new TableName(procedure_name.getSchema(), procedure_name.getName())); 263 264 } 265 266 271 static InternalTableInfo createInternalTableInfo(Transaction transaction) { 272 return new ProcedureInternalTableInfo(transaction); 273 } 274 275 279 public TObject invokeProcedure(ProcedureName procedure_name, 280 TObject[] params) { 281 282 DataTable table = connection.getTable(Database.SYS_FUNCTION); 283 284 Table t = findProcedureEntry(table, procedure_name); 286 if (t.getRowCount() == 0) { 287 throw new StatementException("Procedure " + procedure_name + 288 " doesn't exist."); 289 } 290 291 int row_index = t.rowEnumeration().nextRowIndex(); 292 TObject type_ob = t.getCellContents(2, row_index); 293 TObject location_ob = t.getCellContents(3, row_index); 294 TObject return_type_ob = t.getCellContents(4, row_index); 295 TObject param_types_ob = t.getCellContents(5, row_index); 296 TObject owner_ob = t.getCellContents(6, row_index); 297 298 String type = type_ob.getObject().toString(); 299 String location = location_ob.getObject().toString(); 300 TType return_type = null; 301 if (!return_type_ob.isNull()) { 302 return_type = TType.decodeString(return_type_ob.getObject().toString()); 303 } 304 TType[] param_types = 305 TType.decodeTypes(param_types_ob.getObject().toString()); 306 String owner = owner_ob.getObject().toString(); 307 308 if (params.length != param_types.length) { 310 throw new StatementException( 311 "Parameters given do not match the parameters of the procedure: " + 312 procedureInfoString(procedure_name, return_type, param_types)); 313 } 314 315 if (type.equals("Java-1")) { 317 return invokeJavaV1Procedure(procedure_name, location, 318 return_type, param_types, owner, params); 319 } 320 else { 321 throw new RuntimeException ("Unknown procedure type: " + type); 322 } 323 324 } 325 326 331 private static Class resolveToClass(String java_spec) { 332 java_spec = java_spec.trim(); 334 int dimensions = -1; 336 int last_index = java_spec.length(); 337 while (last_index > 0) { 338 ++dimensions; 339 last_index = java_spec.lastIndexOf("[]", last_index) - 1; 340 } 341 int array_end = java_spec.length() - (dimensions * 2); 343 String class_part = java_spec.substring(0, array_end); 344 if (class_part.indexOf("[]") != -1) { 346 throw new RuntimeException ( 347 "Java class specification incorrectly formatted: " + java_spec); 348 } 349 350 Class cl; 353 if (class_part.indexOf(".") != -1) { 355 try { 357 cl = Class.forName(class_part); 358 } 359 catch (ClassNotFoundException i) { 360 throw new RuntimeException ("Java class not found: " + class_part); 361 } 362 } 363 364 else if (class_part.equals("boolean")) { 366 cl = boolean.class; 367 } 368 else if (class_part.equals("byte")) { 369 cl = byte.class; 370 } 371 else if (class_part.equals("short")) { 372 cl = short.class; 373 } 374 else if (class_part.equals("char")) { 375 cl = char.class; 376 } 377 else if (class_part.equals("int")) { 378 cl = int.class; 379 } 380 else if (class_part.equals("long")) { 381 cl = long.class; 382 } 383 else if (class_part.equals("float")) { 384 cl = float.class; 385 } 386 else if (class_part.equals("double")) { 387 cl = double.class; 388 } 389 else { 390 if (class_part.equals("ProcedureConnection")) { 393 cl = ProcedureConnection.class; 394 } 395 else { 396 try { 397 cl = Class.forName("java.lang." + class_part); 398 } 399 catch (ClassNotFoundException i) { 400 throw new RuntimeException ("Java class not found: " + class_part); 402 } 403 } 404 } 405 406 if (dimensions > 0) { 408 cl = java.lang.reflect.Array.newInstance(cl, 411 new int[dimensions]).getClass(); 412 } 413 414 return cl; 415 416 } 417 418 419 420 421 432 public static Method javaProcedureMethod( 433 String location_str, TType[] param_types) { 434 String [] loc_parts = parseJavaLocationString(location_str); 436 437 String class_name; 439 String method_name; 441 Class [] object_specification; 444 boolean firstProcedureConnectionIgnore; 445 446 if (loc_parts.length == 1) { 447 class_name = loc_parts[0]; 450 method_name = "invoke"; 451 object_specification = new Class [param_types.length]; 453 firstProcedureConnectionIgnore = true; 455 } 456 else { 457 class_name = loc_parts[0]; 460 method_name = loc_parts[1]; 461 object_specification = new Class [loc_parts.length - 2]; 462 463 for (int i = 0; i < loc_parts.length - 2; ++i) { 464 String java_spec = loc_parts[i + 2]; 465 object_specification[i] = resolveToClass(java_spec); 466 } 467 468 firstProcedureConnectionIgnore = false; 469 } 470 471 Class procedure_class; 472 try { 473 procedure_class = Class.forName(class_name); 475 } 476 catch (ClassNotFoundException e) { 477 throw new RuntimeException ("Procedure class not found: " + 478 class_name); 479 } 480 481 Method[] methods = procedure_class.getMethods(); 483 Method invoke_method = null; 484 for (int i = 0; i < methods.length; ++i) { 486 Method method = methods[i]; 487 int modifier = method.getModifiers(); 488 489 if (Modifier.isStatic(modifier) && Modifier.isPublic(modifier) && 490 method.getName().equals(method_name)) { 491 492 boolean params_match; 493 494 Class [] method_args = method.getParameterTypes(); 496 if (method_args.length == 0 && object_specification.length == 0) { 499 params_match = true; 500 } 501 else { 502 int search_start = 0; 503 if (firstProcedureConnectionIgnore && 505 ProcedureConnection.class.isAssignableFrom(method_args[0])) { 506 search_start = 1; 507 } 508 if (object_specification.length == 510 method_args.length - search_start) { 511 boolean match_spec = true; 513 for (int n = 0; 514 n < object_specification.length && match_spec == true; 515 ++n) { 516 Class ob_spec = object_specification[n]; 517 if (ob_spec != null && 518 ob_spec != method_args[n + search_start]) { 519 match_spec = false; 520 } 521 } 522 params_match = match_spec; 523 } 524 else { 525 params_match = false; 526 } 527 } 528 529 if (params_match) { 530 if (invoke_method == null) { 531 invoke_method = method; 532 } 533 else { 534 throw new RuntimeException ("Ambiguous public static " + 535 method_name + " methods in stored procedure class '" + 536 class_name + "'"); 537 } 538 } 539 540 } 541 542 } 543 544 return invoke_method; 546 547 } 548 549 550 551 553 566 private TObject invokeJavaV1Procedure(ProcedureName procedure_name, 567 String location_str, TType return_type, TType[] param_types, 568 String owner, TObject[] param_values) { 569 570 Method invoke_method = javaProcedureMethod(location_str, param_types); 572 573 if (invoke_method == null) { 575 throw new RuntimeException ("Could not find the invokation method for " + 576 "the Java location string '" + location_str + "'"); 577 } 578 579 Class [] java_param_types = invoke_method.getParameterTypes(); 582 583 int start_param; 585 Object [] java_values; 586 if (java_param_types.length > 0 && 587 ProcedureConnection.class.isAssignableFrom(java_param_types[0])) { 588 start_param = 1; 589 java_values = new Object [param_types.length + 1]; 590 } 591 else { 592 start_param = 0; 593 java_values = new Object [param_types.length]; 594 } 595 596 for (int i = 0; i < param_types.length; ++i) { 598 TObject value = param_values[i]; 599 TType proc_type = param_types[i]; 600 Class java_type = java_param_types[i + start_param]; 601 String java_type_str = java_type.getName(); 602 603 if (value.isNull()) { 605 java_values[i + start_param] = null; 606 } 607 else { 608 TType value_type = value.getTType(); 609 if (proc_type.comparableTypes(value_type)) { 611 612 boolean error_cast = false; 613 Object cast_value = null; 614 615 if (value_type instanceof TStringType) { 618 StringAccessor accessor = (StringAccessor) value.getObject(); 621 if (java_type == java.lang.String .class) { 622 cast_value = accessor.toString(); 623 } 624 else if (java_type == java.io.Reader .class) { 625 cast_value = accessor.getReader(); 626 } 627 else { 628 error_cast = true; 629 } 630 } 631 else if (value_type instanceof TBooleanType) { 632 if (java_type == java.lang.Boolean .class || 635 java_type == boolean.class) { 636 cast_value = value.getObject(); 637 } 638 else { 639 error_cast = true; 640 } 641 } 642 else if (value_type instanceof TDateType) { 643 java.util.Date d = (java.util.Date ) value.getObject(); 646 if (java_type == java.util.Date .class) { 647 cast_value = d; 648 } 649 else if (java_type == java.sql.Date .class) { 650 cast_value = new java.sql.Date (d.getTime()); 651 } 652 else if (java_type == java.sql.Time .class) { 653 cast_value = new java.sql.Time (d.getTime()); 654 } 655 else if (java_type == java.sql.Timestamp .class) { 656 cast_value = new java.sql.Timestamp (d.getTime()); 657 } 658 else { 659 error_cast = true; 660 } 661 } 662 else if (value_type instanceof TNumericType) { 663 BigNumber num = (BigNumber) value.getObject(); 665 if (java_type == BigNumber.class) { 666 cast_value = num; 667 } 668 else if (java_type == java.lang.Byte .class || 669 java_type == byte.class) { 670 cast_value = new Byte (num.byteValue()); 671 } 672 else if (java_type == java.lang.Short .class || 673 java_type == short.class) { 674 cast_value = new Short (num.shortValue()); 675 } 676 else if (java_type == java.lang.Integer .class || 677 java_type == int.class) { 678 cast_value = new Integer (num.intValue()); 679 } 680 else if (java_type == java.lang.Long .class || 681 java_type == long.class) { 682 cast_value = new Long (num.longValue()); 683 } 684 else if (java_type == java.lang.Float .class || 685 java_type == float.class) { 686 cast_value = new Float (num.floatValue()); 687 } 688 else if (java_type == java.lang.Double .class || 689 java_type == double.class) { 690 cast_value = new Double (num.doubleValue()); 691 } 692 else if (java_type == java.math.BigDecimal .class) { 693 cast_value = num.asBigDecimal(); 694 } 695 else { 696 error_cast = true; 697 } 698 } 699 else if (value_type instanceof TBinaryType) { 700 BlobAccessor blob = (BlobAccessor) value.getObject(); 703 if (java_type == java.io.InputStream .class) { 704 cast_value = blob.getInputStream(); 705 } 706 else if (java_type == byte[].class) { 707 byte[] buf = new byte[blob.length()]; 708 try { 709 InputStream in = blob.getInputStream(); 710 int n = 0; 711 int len = blob.length(); 712 while (len > 0) { 713 int count = in.read(buf, n, len); 714 if (count == -1) { 715 throw new IOException ("End of stream."); 716 } 717 n += count; 718 len -= count; 719 } 720 } 721 catch (IOException e) { 722 throw new RuntimeException ("IO Error: " + e.getMessage()); 723 } 724 cast_value = buf; 725 } 726 else { 727 error_cast = true; 728 } 729 730 } 731 732 if (error_cast) { 734 throw new StatementException("Unable to cast argument " + i + 735 " ... " + value_type.asSQLString() + " to " + java_type_str + 736 " for procedure: " + 737 procedureInfoString(procedure_name, return_type, param_types)); 738 } 739 740 java_values[i + start_param] = cast_value; 742 743 } 744 else { 745 throw new StatementException("Parameter (" + i + ") not compatible " + 747 value.getTType().asSQLString() + " -> " + proc_type.asSQLString() + 748 " for procedure: " + 749 procedureInfoString(procedure_name, return_type, param_types)); 750 } 751 752 } 754 } 756 User priv_user = new User(owner, connection.getDatabase(), 758 "/Internal/Procedure/", System.currentTimeMillis()); 759 760 ProcedureConnection proc_connection = 762 connection.createProcedureConnection(priv_user); 763 Object result; 764 try { 765 767 if (start_param > 0) { 769 java_values[0] = proc_connection; 770 } 771 772 775 try { 777 result = invoke_method.invoke(null, java_values); 778 } 779 catch (IllegalAccessException e) { 780 connection.Debug().writeException(e); 781 throw new StatementException("Illegal access exception when invoking " + 782 "stored procedure: " + e.getMessage()); 783 } 784 catch (InvocationTargetException e) { 785 Throwable real_e = e.getTargetException(); 786 connection.Debug().writeException(real_e); 787 throw new StatementException("Procedure Exception: " + 788 real_e.getMessage()); 789 } 790 791 } 792 finally { 793 connection.disposeProcedureConnection(proc_connection); 794 } 795 796 if (return_type == null) { 798 return null; 799 } 800 else { 801 return TObject.createAndCastFromObject(return_type, result); 803 } 804 805 } 806 807 809 813 private static class ProcedureInternalTableInfo 814 extends AbstractInternalTableInfo2 { 815 816 ProcedureInternalTableInfo(Transaction transaction) { 817 super(transaction, Database.SYS_FUNCTION); 818 } 819 820 private static DataTableDef createDataTableDef(String schema, String name) { 821 DataTableDef def = new DataTableDef(); 823 def.setTableName(new TableName(schema, name)); 824 825 def.addColumn(DataTableColumnDef.createStringColumn("type")); 827 def.addColumn(DataTableColumnDef.createStringColumn("location")); 828 def.addColumn(DataTableColumnDef.createStringColumn("return_type")); 829 def.addColumn(DataTableColumnDef.createStringColumn("param_args")); 830 def.addColumn(DataTableColumnDef.createStringColumn("owner")); 831 832 def.setImmutable(); 834 835 return def; 837 } 838 839 840 public String getTableType(int i) { 841 return "FUNCTION"; 842 } 843 844 public DataTableDef getDataTableDef(int i) { 845 TableName table_name = getTableName(i); 846 return createDataTableDef(table_name.getSchema(), table_name.getName()); 847 } 848 849 public MutableTableDataSource createInternalTable(int index) { 850 MutableTableDataSource table = 851 transaction.getTable(Database.SYS_FUNCTION); 852 RowEnumeration row_e = table.rowEnumeration(); 853 int p = 0; 854 int i; 855 int row_i = -1; 856 while (row_e.hasMoreRows()) { 857 i = row_e.nextRowIndex(); 858 if (p == index) { 859 row_i = i; 860 } 861 else { 862 ++p; 863 } 864 } 865 if (p == index) { 866 String schema = table.getCellContents(0, row_i).getObject().toString(); 867 String name = table.getCellContents(1, row_i).getObject().toString(); 868 869 final DataTableDef table_def = createDataTableDef(schema, name); 870 final TObject type = table.getCellContents(2, row_i); 871 final TObject location = table.getCellContents(3, row_i); 872 final TObject return_type = table.getCellContents(4, row_i); 873 final TObject param_types = table.getCellContents(5, row_i); 874 final TObject owner = table.getCellContents(6, row_i); 875 876 return new GTDataSource(transaction.getSystem()) { 879 public DataTableDef getDataTableDef() { 880 return table_def; 881 } 882 public int getRowCount() { 883 return 1; 884 } 885 public TObject getCellContents(int col, int row) { 886 switch (col) { 887 case 0: 888 return type; 889 case 1: 890 return location; 891 case 2: 892 return return_type; 893 case 3: 894 return param_types; 895 case 4: 896 return owner; 897 default: 898 throw new RuntimeException ("Column out of bounds."); 899 } 900 } 901 }; 902 903 } 904 else { 905 throw new RuntimeException ("Index out of bounds."); 906 } 907 908 } 909 910 } 911 912 } 913 914 | Popular Tags |