001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.quercus.lib.db;
031:
032: import com.caucho.quercus.annotation.NotNull;
033: import com.caucho.quercus.annotation.Optional;
034: import com.caucho.quercus.annotation.ReturnNullAsFalse;
035: import com.caucho.quercus.env.*;
036: import com.caucho.quercus.module.AbstractQuercusModule;
037: import com.caucho.util.L10N;
038: import com.caucho.util.Log;
039:
040: import java.sql.ResultSetMetaData;
041: import java.sql.SQLException;
042: import java.sql.Statement;
043: import java.sql.Types;
044: import java.util.logging.Level;
045: import java.util.logging.Logger;
046:
047: /**
048: * PHP mysql routines.
049: */
050: public class MysqlModule extends AbstractQuercusModule {
051:
052: private static final Logger log = Log.open(MysqlModule.class);
053: private static final L10N L = new L10N(MysqlModule.class);
054:
055: public static final int MYSQL_ASSOC = 0x1;
056: public static final int MYSQL_NUM = 0x2;
057: public static final int MYSQL_BOTH = 0x3;
058:
059: public static final int MYSQL_USE_RESULT = 0x0;
060: public static final int MYSQL_STORE_RESULT = 0x1;
061:
062: public MysqlModule() {
063: }
064:
065: /**
066: * Returns true for the mysql extension.
067: */
068: public String[] getLoadedExtensions() {
069: return new String[] { "mysql" };
070: }
071:
072: /**
073: * Returns the number of affected rows.
074: */
075: public int mysql_affected_rows(Env env, @Optional
076: Mysqli conn) {
077: if (conn == null)
078: conn = getConnection(env);
079:
080: return conn.affected_rows();
081: }
082:
083: /**
084: * Returns the client encoding
085: */
086: public String mysql_client_encoding(Env env, @Optional
087: Mysqli conn) {
088: if (conn == null)
089: conn = getConnection(env);
090:
091: return conn.client_encoding();
092: }
093:
094: /**
095: * Closes a mysql connection.
096: */
097: public boolean mysql_close(Env env, @Optional
098: Mysqli conn) {
099: Mysqli envConn = (Mysqli) env.getSpecialValue("caucho.mysql");
100:
101: if (conn == null)
102: conn = envConn;
103:
104: if (conn == envConn)
105: env.removeSpecialValue("caucho.mysql");
106:
107: if (conn != null) {
108: conn.close(env);
109:
110: return true;
111: } else
112: return true;
113: }
114:
115: /**
116: * Creates a database.
117: */
118: public boolean mysql_create_db(Env env, @NotNull
119: String name, @Optional
120: Mysqli conn) {
121: if (name == null)
122: return false;
123:
124: if (conn == null)
125: conn = getConnection(env);
126:
127: Statement stmt = null;
128:
129: // XXX: move implementation
130: try {
131: try {
132: stmt = conn.validateConnection().getConnection()
133: .createStatement();
134: stmt.setEscapeProcessing(false);
135: stmt.executeUpdate("CREATE DATABASE " + name);
136: } finally {
137: if (stmt != null)
138: stmt.close();
139: }
140: } catch (SQLException e) {
141: log.log(Level.FINE, e.toString(), e);
142: return false;
143: }
144:
145: return true;
146: }
147:
148: /**
149: * Moves the intenal row pointer of the MySQL result to the
150: * specified row number, 0 based.
151: */
152: public boolean mysql_data_seek(Env env, @NotNull
153: MysqliResult result, int rowNumber) {
154: if (result == null)
155: return false;
156:
157: if (result.seek(env, rowNumber)) {
158: return true;
159: } else {
160: env
161: .warning(L
162: .l(
163: "Offset {0} is invalid for MySQL (or the query data is unbuffered)",
164: rowNumber));
165: return false;
166: }
167: }
168:
169: /**
170: * Retrieves the database name after a call to mysql_list_dbs()
171: */
172: public Value mysql_db_name(Env env, @NotNull
173: MysqliResult result, int row, @Optional("0")
174: Value field) {
175: if (result == null)
176: return BooleanValue.FALSE;
177:
178: return mysql_result(env, result, row, field);
179: }
180:
181: /**
182: * Returns the value of one field in the result set.
183: */
184: public Value mysql_result(Env env, @NotNull
185: MysqliResult result, int row, @Optional("0")
186: Value field) {
187: if (result == null)
188: return BooleanValue.FALSE;
189:
190: return result.getResultField(env, row, field);
191: }
192:
193: /**
194: * Drops a database.
195: */
196: public boolean mysql_drop_db(Env env, @NotNull
197: String databaseName, @Optional
198: Mysqli conn) {
199: if (databaseName == null)
200: return false;
201:
202: Value value = mysql_query(env, "DROP DATABASE " + databaseName,
203: conn);
204:
205: return (value != null && value.toBoolean());
206: }
207:
208: /**
209: * Returns the error number of the most recent error
210: */
211: public int mysql_errno(Env env, @Optional
212: Mysqli conn) {
213: if (conn == null)
214: conn = getConnection(env);
215:
216: String error = conn.error();
217:
218: int errno = conn.errno();
219:
220: if (errno != 0)
221: return errno;
222: else if (error != null && !"".equals(error))
223: return 2006; // mysql has gone away
224: else
225: return 0;
226: }
227:
228: /**
229: * Returns the most recent error.
230: */
231: public String mysql_error(Env env, @Optional
232: Mysqli conn) {
233: if (conn == null)
234: conn = getConnection(env);
235:
236: String error = conn.error();
237:
238: if (error == null)
239: return "";
240:
241: return error;
242: }
243:
244: public Value mysql_escape_string(Env env,
245: StringValue unescapedString) {
246: return mysql_real_escape_string(env, unescapedString, null);
247: }
248:
249: /**
250: * Escapes special characters.
251: *
252: * @see String QuercusMysqliModule.mysqli_real_escape_string(JdbcConnectionResource, String)
253: *
254: * @return the escaped string
255: */
256:
257: public Value mysql_real_escape_string(Env env,
258: StringValue unescapedString, @Optional
259: Mysqli conn) {
260: if (conn == null)
261: conn = getConnection(env);
262:
263: return conn.real_escape_string(unescapedString);
264: }
265:
266: /**
267: * Returns a row from the connection
268: */
269: public Value mysql_fetch_array(Env env, @NotNull
270: MysqliResult result, @Optional("MYSQL_BOTH")
271: int type) {
272: if (result == null)
273: return BooleanValue.FALSE;
274:
275: Value value = result.fetch_array(env, type);
276:
277: if (value != null)
278: return value;
279: else
280: return BooleanValue.FALSE;
281: }
282:
283: /**
284: * Returns a row from the connection
285: */
286: @ReturnNullAsFalse
287: public ArrayValue mysql_fetch_assoc(Env env, @NotNull
288: MysqliResult result) {
289: if (result == null)
290: return null;
291:
292: return result.fetch_array(env, MYSQL_ASSOC);
293: }
294:
295: /**
296: * Returns an object containing field information.
297: * On success, this method increments the field offset
298: * (see {@link #mysql_field_seek}).
299: *
300: * <h3>ERRATA</h3>
301: * <ul>
302: * <li>quercus returns "string" for BIT type, php returns "unknown"
303: * <li>quercus always returns int(0) for unique_key
304: * <li>quercus always returns int(0) for zerofill
305: * <li>quercus always returns int(0) for multiple_key
306: * </ul>
307: *
308: */
309: public Value mysql_fetch_field(Env env, @NotNull
310: MysqliResult result, @Optional("-1")
311: int fieldOffset) {
312: /**
313: * ERRATA is also documented in php/142s.qa
314: * There is probably a mysql specific query or API that would be better
315: * for getting this information
316: */
317:
318: if (result == null)
319: return BooleanValue.FALSE;
320:
321: // php/142v.qa - call must succeed even if some info not available
322:
323: try {
324: if (fieldOffset == -1) {
325: fieldOffset = result.field_tell(env);
326: result.setFieldOffset(fieldOffset + 1);
327: }
328:
329: ResultSetMetaData md = result.getMetaData();
330:
331: if (md.getColumnCount() <= fieldOffset || fieldOffset < 0) {
332: env.invalidArgument("field", fieldOffset);
333: return BooleanValue.FALSE;
334: }
335:
336: int jdbcField = fieldOffset + 1;
337: int jdbcColumnType = md.getColumnType(jdbcField);
338:
339: String catalogName = md.getCatalogName(jdbcField);
340: String tableName = md.getTableName(jdbcField);
341: String columnName = md.getColumnName(jdbcField);
342:
343: // some information is not available from the ResultSetMetaData
344: JdbcColumnMetaData columnMd = null;
345:
346: JdbcConnectionResource conn = getConnection(env)
347: .validateConnection();
348:
349: JdbcTableMetaData tableMd = conn.getTableMetaData(
350: catalogName, null, tableName);
351:
352: if (tableMd != null)
353: columnMd = tableMd.getColumn(columnName);
354:
355: // XXX: maxlen note from PHP comments:
356: // the length of the longest value for that field in the returned dataset,
357: // NOT the maximum length of data that column is designed to hold.
358:
359: int maxLength = 0;
360: int notNull = md.isNullable(jdbcField) == ResultSetMetaData.columnNullable ? 0
361: : 1;
362: int numeric = JdbcColumnMetaData.isNumeric(jdbcColumnType) ? 1
363: : 0;
364: int blob = JdbcColumnMetaData.isBlob(jdbcColumnType) ? 1
365: : 0;
366: String type = JdbcResultResource
367: .getColumnPHPName(jdbcColumnType);
368: int unsigned = md.isSigned(jdbcField) ? 0 : numeric;
369:
370: if (jdbcColumnType == Types.BOOLEAN
371: || jdbcColumnType == Types.BIT)
372: unsigned = 0;
373: else if (jdbcColumnType == Types.DECIMAL)
374: numeric = 1;
375:
376: int zerofill = 0;
377: int primaryKey = 0;
378: int multipleKey = 0;
379: int uniqueKey = 0;
380:
381: if (columnMd != null) {
382: zerofill = columnMd.isZeroFill() ? 1 : 0;
383: primaryKey = columnMd.isPrimaryKey() ? 1 : 0;
384: // XXX: not sure what multipleKey is supposed to be
385: // multipleKey = columnMd.isIndex() && !columnMd.isPrimaryKey() ? 1 : 0;
386: uniqueKey = columnMd.isUnique() ? 1 : 0;
387: } else
388: notNull = 1;
389:
390: ObjectValue fieldResult = env.createObject();
391:
392: fieldResult.putField(env, "name", columnName);
393: fieldResult.putField(env, "table", tableName);
394: fieldResult.putField(env, "def", "");
395: fieldResult.putField(env, "max_length", maxLength);
396: fieldResult.putField(env, "not_null", notNull);
397: fieldResult.putField(env, "primary_key", primaryKey);
398: fieldResult.putField(env, "multiple_key", multipleKey);
399: fieldResult.putField(env, "unique_key", uniqueKey);
400: fieldResult.putField(env, "numeric", numeric);
401: fieldResult.putField(env, "blob", blob);
402: fieldResult.putField(env, "type", type);
403: fieldResult.putField(env, "unsigned", unsigned);
404: fieldResult.putField(env, "zerofill", zerofill);
405:
406: return fieldResult;
407: } catch (SQLException e) {
408: log.log(Level.FINE, e.toString(), e);
409: return BooleanValue.FALSE;
410: }
411: }
412:
413: /**
414: * Executes a query and returns a result set.
415: *
416: * Returns true on update success, false on failure, and a result set
417: * for a successful select
418: */
419: public Value mysql_query(Env env, String sql, @Optional
420: Mysqli conn) {
421: if (conn == null)
422: conn = getConnection(env);
423:
424: return conn.query(env, sql, MYSQL_STORE_RESULT);
425: }
426:
427: /**
428: * Returns an array of lengths.
429: */
430: public Value mysql_fetch_lengths(Env env, @NotNull
431: MysqliResult result) {
432: if (result == null)
433: return BooleanValue.FALSE;
434:
435: return result.fetch_lengths();
436: }
437:
438: /**
439: * Returns an object with properties that correspond to the fetched row
440: * and moves the data pointer ahead.
441: */
442: public Value mysql_fetch_object(Env env, @NotNull
443: MysqliResult result) {
444: if (result == null)
445: return BooleanValue.FALSE;
446:
447: Value value = result.fetch_object(env);
448:
449: // php/142t
450: // must return FALSE for mediawiki
451: if (value == NullValue.NULL)
452: value = BooleanValue.FALSE;
453:
454: return value;
455: }
456:
457: /**
458: * Returns a row from the connection
459: */
460: @ReturnNullAsFalse
461: public ArrayValue mysql_fetch_row(Env env, @NotNull
462: MysqliResult result) {
463: if (result == null)
464: return null;
465:
466: return result.fetch_row(env);
467: }
468:
469: /**
470: * Returns the field flags of the specified field. The flags are reported as
471: * a space separated list of words, the returned value can be split using explode().
472: *
473: * The following flages are reported, older version of MySQL may not report all flags:
474: * <ul>
475: * <li> not_null
476: * <li> primary_key
477: * <li> multiple_key
478: * <li> blob
479: * <li> unsigned
480: * <li> zerofill
481: * <li> binary
482: * <li> enum
483: * <li> auto_increment
484: * <li> timestamp
485: * </ul>
486: */
487: public Value mysql_field_flags(Env env, @NotNull
488: MysqliResult result, int fieldOffset) {
489: if (result == null)
490: return BooleanValue.FALSE;
491:
492: Value fieldName = result.getFieldName(env, fieldOffset);
493:
494: if (fieldName == BooleanValue.FALSE)
495: return BooleanValue.FALSE;
496:
497: Value fieldTable = result.getFieldTable(env, fieldOffset);
498:
499: if (fieldTable == BooleanValue.FALSE)
500: return BooleanValue.FALSE;
501:
502: String sql = "SHOW FULL COLUMNS FROM " + fieldTable.toString()
503: + " LIKE \'" + fieldName.toString() + "\'";
504:
505: Mysqli conn = getConnection(env);
506:
507: Object metaResult = conn.validateConnection().realQuery(sql);
508:
509: if (metaResult instanceof MysqliResult)
510: return ((MysqliResult) metaResult).getFieldFlags();
511:
512: return BooleanValue.FALSE;
513: }
514:
515: /**
516: * Returns field name at given offset.
517: */
518: public Value mysql_field_name(Env env, @NotNull
519: MysqliResult result, int fieldOffset) {
520: if (result == null)
521: return BooleanValue.FALSE;
522:
523: return result.getFieldName(env, fieldOffset);
524: }
525:
526: /**
527: * Seeks to the specified field offset, the field offset is
528: * is used as the default for the next call to {@link #mysql_fetch_field}.
529: */
530: public boolean mysql_field_seek(Env env, @NotNull
531: MysqliResult result, int fieldOffset) {
532: if (result == null)
533: return false;
534:
535: return result.field_seek(env, fieldOffset);
536: }
537:
538: /**
539: * Returns the table corresponding to the field.
540: */
541: public Value mysql_field_table(Env env, @NotNull
542: MysqliResult result, int fieldOffset) {
543: if (result == null)
544: return BooleanValue.FALSE;
545:
546: return result.getFieldTable(env, fieldOffset);
547: }
548:
549: /**
550: * Returns the field type.
551: */
552: public static Value mysql_field_type(Env env, @NotNull
553: MysqliResult result, Value fieldOffset) {
554: if (result == null) {
555: return NullValue.NULL;
556: }
557:
558: if (!fieldOffset.isset())
559: return NullValue.NULL;
560:
561: return result.getFieldType(env, fieldOffset.toInt());
562: }
563:
564: /**
565: * Deprecated alias for mysql_field_len.
566: */
567: public static Value mysql_fieldlen(Env env, @NotNull
568: MysqliResult result, int fieldOffset) {
569: return mysql_field_len(env, result, fieldOffset);
570: }
571:
572: /**
573: * Returns the length of the specified field
574: */
575: public static Value mysql_field_len(Env env, @NotNull
576: MysqliResult result, @Optional
577: int fieldOffset) {
578: // gallery2 calls this function with 1 arg, so fieldOffset is optional
579:
580: if (result == null)
581: return BooleanValue.FALSE;
582:
583: // ERRATUM: Returns 10 for datatypes DEC and NUMERIC instead of 11
584:
585: return result.getFieldLength(env, fieldOffset);
586: }
587:
588: /**
589: * Frees a mysql result.
590: */
591: public boolean mysql_free_result(MysqliResult result) {
592: if (result != null)
593: result.close();
594:
595: return true;
596: }
597:
598: /**
599: * Returns the MySQL client version.
600: */
601: public static StringValue mysql_get_client_info(Env env) {
602: return Mysqli.getClientInfo(env);
603: }
604:
605: /**
606: * Returns a string describing the host.
607: */
608: public String mysql_get_host_info(Env env, @Optional
609: Mysqli conn) {
610: if (conn == null)
611: conn = getConnection(env);
612:
613: return conn.get_host_info();
614: }
615:
616: /**
617: * Returns an integer respresenting the MySQL protocol
618: * version.
619: */
620: public int mysql_get_proto_info(Env env, @Optional
621: Mysqli conn) {
622: if (conn == null)
623: conn = getConnection(env);
624:
625: return conn.get_proto_info();
626: }
627:
628: /**
629: * Returns the MySQL server version.
630: */
631: @ReturnNullAsFalse
632: public String mysql_get_server_info(Env env, @Optional
633: Mysqli conn) {
634: if (conn == null) {
635: conn = getConnection(env);
636: }
637:
638: if (conn != null && conn.isConnected())
639: return conn.get_server_info();
640: else
641: return null;
642: }
643:
644: /**
645: * returns ID generated for an AUTO_INCREMENT column by the previous
646: * INSERT query on success, 0 if the previous query does not generate
647: * an AUTO_INCREMENT value, or FALSE if no MySQL connection was established
648: */
649: public Value mysql_insert_id(Env env, @Optional
650: Mysqli conn) {
651: if (conn == null)
652: conn = getConnection(env);
653:
654: return conn.insert_id();
655: }
656:
657: /**
658: * Returns a result pointer containing the databases available from the current mysql daemon.
659: */
660: public JdbcResultResource mysql_list_dbs(Env env, @Optional
661: Mysqli conn) {
662: if (conn == null)
663: conn = getConnection(env);
664:
665: return conn.list_dbs();
666: }
667:
668: /**
669: * Retrieves information about the given table name
670: */
671: public Value mysql_list_fields(Env env, String databaseName,
672: String tableName, @Optional
673: Mysqli conn) {
674: if (databaseName == null)
675: return BooleanValue.FALSE;
676:
677: if (tableName == null)
678: return BooleanValue.FALSE;
679:
680: return mysql_db_query(env, databaseName, "SELECT * FROM "
681: + tableName + " WHERE NULL", conn);
682: }
683:
684: /**
685: * Returns result set or false on error
686: */
687: public Value mysql_db_query(Env env, String databaseName,
688: String query, @Optional
689: Mysqli conn) {
690: if (conn == null)
691: conn = getConnection(env);
692:
693: if (!conn.select_db(databaseName))
694: return BooleanValue.FALSE;
695:
696: return conn.query(env, query, 1);
697: }
698:
699: /**
700: * Selects the database
701: */
702: public boolean mysql_select_db(Env env, String name, @Optional
703: Mysqli conn) {
704: if (conn == null)
705: return getConnection(env, name).select_db(name);
706: else
707: return conn.select_db(name);
708: }
709:
710: /**
711: * Retrieves a list of table names from a MySQL database.
712: */
713: public Object mysql_list_tables(Env env, String databaseName,
714: @Optional
715: Mysqli conn) {
716: return mysql_query(env, "SHOW TABLES FROM " + databaseName,
717: conn);
718: }
719:
720: public int mysql_num_fields(Env env, @NotNull
721: MysqliResult result) {
722: if (result == null)
723: return -1;
724:
725: return result.num_fields();
726: }
727:
728: /**
729: * Retrieves the number of rows in a result set.
730: */
731: public Value mysql_num_rows(Env env, @NotNull
732: MysqliResult result) {
733: if (result == null)
734: return BooleanValue.FALSE;
735:
736: return result.num_rows();
737: }
738:
739: /**
740: * Undocumented alias for {#link #mysql_num_rows}.
741: */
742: public Value mysql_numrows(Env env, @NotNull
743: MysqliResult result) {
744: return mysql_num_rows(env, result);
745: }
746:
747: /**
748: * Returns a new mysql connection.
749: */
750: public Value mysql_pconnect(Env env, @Optional
751: String server, @Optional
752: String user, @Optional
753: String password, @Optional
754: Value newLinkV, @Optional
755: Value flagsV) {
756: return mysql_connect(env, server, user, password, newLinkV,
757: flagsV);
758: }
759:
760: /**
761: * Returns a new mysql connection.
762: */
763: public Value mysql_connect(Env env, @Optional
764: String host, @Optional
765: String userName, @Optional
766: String password, @Optional
767: Value newLinkV, @Optional
768: Value flagsV) {
769: int port = 3306;
770: int length = host.length();
771:
772: if (length == 0) {
773: host = env.getIniString("mysql.default_host");
774: if (host == null)
775: host = "localhost";
776: }
777:
778: int portIndex = host.lastIndexOf(':');
779:
780: // Split host name and port from each other.
781: // Use > 0 because sockets have a ':' at index 0.
782: if (portIndex > 0) {
783: port = 0;
784:
785: for (int j = portIndex + 1; j < length; j++) {
786: char ch = host.charAt(j);
787:
788: if ('0' <= ch && ch <= '9')
789: port = port * 10 + ch - '0';
790: else
791: break;
792: }
793:
794: host = host.substring(0, portIndex);
795: }
796:
797: Mysqli mysqli = new Mysqli(env, host, userName, password, "",
798: port, "", flagsV.toInt(), null, null);
799:
800: if (!mysqli.isConnected())
801: return BooleanValue.FALSE;
802:
803: Value value = env.wrapJava(mysqli);
804:
805: env.setSpecialValue("caucho.mysql", mysqli);
806:
807: return value;
808: }
809:
810: /**
811: * Checks if the connection is still valid.
812: */
813: public boolean mysql_ping(Env env, @Optional
814: Mysqli conn) {
815: if (conn == null)
816: conn = getConnection(env);
817:
818: return conn.ping();
819: }
820:
821: /**
822: * Returns a string with the status of the connection
823: * or NULL if error.
824: */
825: public Value mysql_stat(Env env, @Optional
826: Mysqli conn) {
827: if (conn == null)
828: conn = getConnection(env);
829:
830: Value result = conn.stat(env);
831:
832: return result == BooleanValue.FALSE ? NullValue.NULL : result;
833: }
834:
835: /**
836: * Retrieves the table name corresponding to a field, using
837: * a result return by {@link #mysql_list_tables}.
838: */
839: public Value mysql_tablename(Env env, @NotNull
840: MysqliResult result, int i) {
841: if (result == null)
842: return BooleanValue.FALSE;
843:
844: return result.getResultField(env, i, LongValue.ZERO);
845: }
846:
847: /**
848: * Queries the database.
849: */
850: public Object mysql_unbuffered_query(Env env, @NotNull
851: String name, @Optional
852: Mysqli conn) {
853: return mysql_query(env, name, conn);
854: }
855:
856: //@todo mysql_change_user()
857: //@todo mysql_info()
858: //@todo mysql_list_processes()
859: //@todo mysql_thread_id()
860:
861: private Mysqli getConnection(Env env) {
862: return getConnection(env, "");
863: }
864:
865: private Mysqli getConnection(Env env, String db) {
866: Mysqli conn = (Mysqli) env.getSpecialValue("caucho.mysql");
867:
868: // php/1436
869: if (conn != null)
870: return conn;
871:
872: conn = new Mysqli(env, "localhost", "", "", db, 3306, "", 0,
873: null, null);
874:
875: env.setSpecialValue("caucho.mysql", conn);
876:
877: return conn;
878: }
879: }
|