List:Commits« Previous MessageNext Message »
From:mmatthews Date:May 11 2007 9:20pm
Subject:Connector/J commit: r6424 - branches/branch_5_1/connector-j/src/com/mysql/jdbc
View as plain text  
Modified:
   branches/branch_5_1/connector-j/src/com/mysql/jdbc/DatabaseMetaData.java
   branches/branch_5_1/connector-j/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java
   branches/branch_5_1/connector-j/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java
Log:
Implement some missing JDBC-4.0 functionality, push non-Java-6-specific code back into DatabaseMetadata.

Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/DatabaseMetaData.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/DatabaseMetaData.java	2007-05-10 21:23:16 UTC (rev 6423)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/DatabaseMetaData.java	2007-05-11 21:20:14 UTC (rev 6424)
@@ -431,7 +431,7 @@
 			}
 		}
 	}
-	
+       
 	private static String mysqlKeywordsThatArentSQL92;
 	
 	protected static final int MAX_IDENTIFIER_LENGTH = 64;
@@ -823,14 +823,21 @@
 
 	private byte[][] convertTypeDescriptorToProcedureRow(
 			byte[] procNameAsBytes, String paramName, boolean isOutParam,
-			boolean isInParam, boolean isReturnParam, TypeDescriptor typeDesc)
+			boolean isInParam, boolean isReturnParam, TypeDescriptor typeDesc,
+			boolean forGetFunctionColumns,
+			int ordinal)
 			throws SQLException {
-		byte[][] row = new byte[14][];
+		byte[][] row = forGetFunctionColumns ? new byte[17][] : new byte[14][];
 		row[0] = null; // PROCEDURE_CAT
 		row[1] = null; // PROCEDURE_SCHEM
 		row[2] = procNameAsBytes; // PROCEDURE/NAME
 		row[3] = s2b(paramName); // COLUMN_NAME
 		// COLUMN_TYPE
+		
+		// NOTE: For JDBC-4.0, we luck out here for functions
+		// because the values are the same for functionColumn....
+		// and they're not using Enumerations....
+		
 		if (isInParam && isOutParam) {
 			row[4] = s2b(String.valueOf(procedureColumnInOut));
 		} else if (isInParam) {
@@ -851,17 +858,17 @@
 		// Map 'column****' to 'procedure****'
 		switch (typeDesc.nullability) {
 		case columnNoNulls:
-			row[11] = s2b(Integer.toString(procedureNoNulls)); // NULLABLE
+			row[11] = s2b(String.valueOf(procedureNoNulls)); // NULLABLE
 
 			break;
 
 		case columnNullable:
-			row[11] = s2b(Integer.toString(procedureNullable)); // NULLABLE
+			row[11] = s2b(String.valueOf(procedureNullable)); // NULLABLE
 
 			break;
 
 		case columnNullableUnknown:
-			row[11] = s2b(Integer.toString(procedureNullableUnknown)); // nullable
+			row[11] = s2b(String.valueOf(procedureNullableUnknown)); // nullable
 
 			break;
 
@@ -870,7 +877,22 @@
 					"Internal error while parsing callable statement metadata (unknown nullability value fount)",
 					SQLError.SQL_STATE_GENERAL_ERROR);
 		}
+		
 		row[12] = null;
+		
+		if (forGetFunctionColumns) {
+			// CHAR_OCTECT_LENGTH
+			row[13] = null;
+			
+			// ORDINAL_POSITION
+			row[14] = s2b(String.valueOf(ordinal));
+			
+			// IS_NULLABLE
+			row[15] = Constants.EMPTY_BYTE_ARRAY;
+			
+			row[16] = s2b(paramName);
+		}
+		
 		return row;
 	}
 
@@ -1432,6 +1454,13 @@
 	 */
 	private void getCallStmtParameterTypes(String catalog, String procName,
 			String parameterNamePattern, List resultRows) throws SQLException {
+		getCallStmtParameterTypes(catalog, procName, 
+				parameterNamePattern, resultRows, false);
+	}
+	
+	private void getCallStmtParameterTypes(String catalog, String procName,
+			String parameterNamePattern, List resultRows, 
+			boolean forGetFunctionColumns) throws SQLException {
 		java.sql.Statement paramRetrievalStmt = null;
 		java.sql.ResultSet paramRetrievalRs = null;
 
@@ -1458,7 +1487,7 @@
 		String quoteChar = getIdentifierQuoteString();
 
 		String parameterDef = null;
-
+	
 		boolean isProcedureInAnsiMode = false;
 		String storageDefnDelims = null;
 		String storageDefnClosures = null;
@@ -1627,7 +1656,7 @@
 
 					resultRows.add(convertTypeDescriptorToProcedureRow(
 							procNameAsBytes, "", false, false, true,
-							returnDescriptor));
+							returnDescriptor, forGetFunctionColumns, 0));
 				}
 
 				if ((openParenIndex == -1)
@@ -1671,6 +1700,8 @@
 		}
 
 		if (parameterDef != null) {
+			int ordinal = 1;
+			
 			List parseList = StringUtils.split(parameterDef, ",",
 					storageDefnDelims, storageDefnClosures, true);
 
@@ -1763,7 +1794,8 @@
 					if (wildCompareRes != StringUtils.WILD_COMPARE_NO_MATCH) {
 						byte[][] row = convertTypeDescriptorToProcedureRow(
 								procNameAsBytes, paramName, isOutParam,
-								isInParam, false, typeDesc);
+								isInParam, false, typeDesc, forGetFunctionColumns,
+								ordinal++);
 
 						resultRows.add(row);
 					}
@@ -3986,7 +4018,6 @@
 	public java.sql.ResultSet getProcedureColumns(String catalog,
 			String schemaPattern, String procedureNamePattern,
 			String columnNamePattern) throws SQLException {
-
 		Field[] fields = new Field[13];
 
 		fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
@@ -4002,21 +4033,36 @@
 		fields[10] = new Field("", "RADIX", Types.SMALLINT, 0);
 		fields[11] = new Field("", "NULLABLE", Types.SMALLINT, 0);
 		fields[12] = new Field("", "REMARKS", Types.CHAR, 0);
+		
+		return getProcedureOrFunctionColumns(
+				fields, catalog, schemaPattern,
+				procedureNamePattern, columnNamePattern,
+				true, true);
+	}
+	
+	protected java.sql.ResultSet getProcedureOrFunctionColumns(
+			Field[] fields, String catalog, String schemaPattern,
+			String procedureOrFunctionNamePattern,
+			String columnNamePattern, boolean returnProcedures,
+			boolean returnFunctions) throws SQLException {
 
 		List proceduresToExtractList = new ArrayList();
 
 		if (supportsStoredProcedures()) {
-			if ((procedureNamePattern.indexOf("%") == -1)
-					&& (procedureNamePattern.indexOf("?") == -1)) {
-				proceduresToExtractList.add(procedureNamePattern);
+			if ((procedureOrFunctionNamePattern.indexOf("%") == -1)
+					&& (procedureOrFunctionNamePattern.indexOf("?") == -1)) {
+				proceduresToExtractList.add(procedureOrFunctionNamePattern);
 			} else {
 				
 				ResultSet procedureNameRs = null;
 
 				try {
 
-					procedureNameRs = getProcedures(catalog, schemaPattern,
-							procedureNamePattern);
+					procedureNameRs = getProceduresAndOrFunctions(
+							createFieldMetadataForGetProcedures(),
+							catalog, schemaPattern,
+							procedureOrFunctionNamePattern, returnProcedures,
+							returnFunctions);
 
 					while (procedureNameRs.next()) {
 						proceduresToExtractList.add(procedureNameRs
@@ -4052,7 +4098,8 @@
 			String procName = (String) iter.next();
 
 			getCallStmtParameterTypes(catalog, procName, columnNamePattern,
-					resultRows);
+					resultRows, 
+					fields.length == 17 /* for getFunctionColumns */);
 		}
 
 		return buildResultSet(fields, resultRows);
@@ -4101,6 +4148,13 @@
 	public java.sql.ResultSet getProcedures(String catalog,
 			String schemaPattern, String procedureNamePattern)
 			throws SQLException {
+		Field[] fields = createFieldMetadataForGetProcedures();
+		
+		return getProceduresAndOrFunctions(fields, catalog, schemaPattern,
+				procedureNamePattern, true, true);
+	}
+
+	private Field[] createFieldMetadataForGetProcedures() {
 		Field[] fields = new Field[8];
 		fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
 		fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0);
@@ -4110,9 +4164,7 @@
 		fields[5] = new Field("", "reserved3", Types.CHAR, 0);
 		fields[6] = new Field("", "REMARKS", Types.CHAR, 0);
 		fields[7] = new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 0);
-		
-		return getProceduresAndOrFunctions(fields, catalog, schemaPattern,
-				procedureNamePattern, true, true);
+		return fields;
 	}
 	
 	protected java.sql.ResultSet getProceduresAndOrFunctions(
@@ -6690,7 +6742,7 @@
 	 *            DOCUMENT ME!
 	 * @return DOCUMENT ME!
 	 */
-	private byte[] s2b(String s) throws SQLException {
+	protected byte[] s2b(String s) throws SQLException {
 		return StringUtils.s2b(s, this.conn);
 	}
 	
@@ -7766,4 +7818,60 @@
 	public boolean usesLocalFiles() throws SQLException {
 		return false;
 	}
+	
+	//
+	// JDBC-4.0 functions that aren't reliant on Java6
+    /**
+     * Retrieves a description of the given catalog's system or user 
+     * function parameters and return type.
+     *
+ 	 * @see java.sql.DatabaseMetaData#getFunctionColumns(String, String, String, String)
+     * @since 1.6
+     */
+    public ResultSet getFunctionColumns(String catalog, 
+    		String schemaPattern, 
+    		String functionNamePattern, 
+    		String columnNamePattern) throws SQLException {
+    	Field[] fields = {
+    			new Field("", "FUNCTION_CAT", Types.VARCHAR, 0),
+    			new Field("", "FUNCTION_SCHEM", Types.VARCHAR, 0),
+    			new Field("", "FUNCTION_NAME", Types.VARCHAR, 0),
+    			new Field("", "COLUMN_NAME", Types.VARCHAR, 0),
+    			new Field("", "COLUMN_TYPE", Types.VARCHAR, 0),
+    			new Field("", "DATA_TYPE", Types.SMALLINT, 0),
+    			new Field("", "TYPE_NAME", Types.VARCHAR, 0),
+    			new Field("", "PRECISION", Types.INTEGER, 0),
+    			new Field("", "LENGTH", Types.INTEGER, 0),
+    			new Field("", "SCALE", Types.SMALLINT, 0),
+    			new Field("", "RADIX", Types.SMALLINT, 0),
+    			new Field("", "NULLABLE", Types.SMALLINT, 0),
+    			new Field("", "REMARKS", Types.VARCHAR, 0),
+    			new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 0),
+    			new Field("", "ORDINAL_POSITION", Types.INTEGER, 0),
+    			new Field("", "IS_NULLABLE", Types.VARCHAR, 3),
+    			new Field("", "SPECIFIC_NAME", Types.VARCHAR, 0)};
+
+		return getProcedureOrFunctionColumns(
+				fields, catalog, schemaPattern,
+				functionNamePattern, columnNamePattern,
+				false, true);
+	}
+
+	public boolean providesQueryObjectGenerator() throws SQLException {
+		return false;
+	}
+	
+	public ResultSet getSchemas(String catalog, 
+			String schemaPattern) throws SQLException {
+		Field[] fields = {
+				new Field("", "TABLE_SCHEM", Types.VARCHAR, 255),
+				new Field("", "TABLE_CATALOG", Types.VARCHAR, 255)
+		};
+		
+		return buildResultSet(fields, new ArrayList());
+	}
+
+	public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException {
+		return true;
+	}
 }

Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java	2007-05-10 21:23:16 UTC (rev 6423)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java	2007-05-11 21:20:14 UTC (rev 6424)
@@ -27,109 +27,19 @@
 import java.sql.RowIdLifetime;
 import java.sql.SQLException;
 import java.sql.Types;
+import java.util.ArrayList;
 
-import com.mysql.jdbc.Connection;
-import com.mysql.jdbc.DatabaseMetaData;
-import com.mysql.jdbc.Field;
-import com.mysql.jdbc.exceptions.NotYetImplementedException;
+import java.util.List;
 
 public class JDBC4DatabaseMetaData extends DatabaseMetaData {
 	public JDBC4DatabaseMetaData(ConnectionImpl connToSet, String databaseToSet) {
 		super(connToSet, databaseToSet);
 	}
 
-	public boolean autoCommitFailureClosesAllResultSets() throws SQLException {
-		return false;
-	}
-
-	public ResultSet getClientInfoProperties() throws SQLException {
-		throw new NotYetImplementedException();
-	}
-
-	public ResultSet getFunctionParameters(String catalog, String schemaPattern, String functionNamePattern, String parameterNamePattern) throws SQLException {
-		throw new NotYetImplementedException();
-	}
-
-    /**
-     * Retrieves a description of the  system and user functions available 
-     * in the given catalog.
-     * <P>
-     * Only system and user function descriptions matching the schema and
-     * function name criteria are returned.  They are ordered by
-     * <code>FUNCTION_CAT</code>, <code>FUNCTION_SCHEM</code>,
-     * <code>FUNCTION_NAME</code> and 
-     * <code>SPECIFIC_ NAME</code>.
-     *
-     * <P>Each function description has the the following columns:
-     *  <OL>
-     *	<LI><B>FUNCTION_CAT</B> String => function catalog (may be <code>null</code>)
-     *	<LI><B>FUNCTION_SCHEM</B> String => function schema (may be <code>null</code>)
-     *	<LI><B>FUNCTION_NAME</B> String => function name.  This is the name 
-     * used to invoke the function
-     *	<LI><B>REMARKS</B> String => explanatory comment on the function
-     * <LI><B>FUNCTION_TYPE</B> short => kind of function:
-     *      <UL>
-     *      <LI>functionResultUnknown - Cannot determine if a return value
-     *       or table will be returned
-     *      <LI> functionNoTable- Does not return a table
-     *      <LI> functionReturnsTable - Returns a table
-     *      </UL>
-     *	<LI><B>SPECIFIC_NAME</B> String  => the name which uniquely identifies 
-     *  this function within its schema.  This is a user specified, or DBMS
-     * generated, name that may be different then the <code>FUNCTION_NAME</code> 
-     * for example with overload functions
-     *  </OL>
-     * <p>
-     * A user may not have permission to execute any of the functions that are
-     * returned by <code>getFunctions</code>
-     *
-     * @param catalog a catalog name; must match the catalog name as it
-     *        is stored in the database; "" retrieves those without a catalog;
-     *        <code>null</code> means that the catalog name should not be used to narrow
-     *        the search
-     * @param schemaPattern a schema name pattern; must match the schema name
-     *        as it is stored in the database; "" retrieves those without a schema;
-     *        <code>null</code> means that the schema name should not be used to narrow
-     *        the search
-     * @param functionNamePattern a function name pattern; must match the
-     *        function name as it is stored in the database 
-     * @return <code>ResultSet</code> - each row is a function description 
-     * @exception SQLException if a database access error occurs
-     * @see #getSearchStringEscape 
-     * @since 1.6
-     */
-	public ResultSet getFunctions(String catalog, String schemaPattern,
-			String functionNamePattern) throws SQLException {
-
-		Field[] fields = new Field[6];
-		fields[0] = new Field("", "FUNCTION_CAT", Types.CHAR,
-				MAX_IDENTIFIER_LENGTH);
-		fields[1] = new Field("", "FUNCTION_SCHEM", Types.CHAR,
-				MAX_IDENTIFIER_LENGTH);
-		fields[2] = new Field("", "FUNCTION_NAME", Types.CHAR,
-				MAX_IDENTIFIER_LENGTH);
-		fields[3] = new Field("", "REMARKS", Types.CHAR, MAX_IDENTIFIER_LENGTH);
-		fields[4] = new Field("", "FUNCTION_TYPE", Types.SMALLINT, 0);
-		fields[5] = new Field("", "SPECIFIC_NAME", Types.CHAR,
-				MAX_IDENTIFIER_LENGTH);
-
-		return getProceduresAndOrFunctions(fields, catalog, schemaPattern,
-				functionNamePattern, false, true);
-
-	}
-
 	public RowIdLifetime getRowIdLifetime() throws SQLException {
-		throw new NotYetImplementedException();
+		return RowIdLifetime.ROWID_UNSUPPORTED;
 	}
 
-	public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
-		throw new NotYetImplementedException();
-	}
-
-	public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException {
-		return true;
-	}
-
 	/**
      * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
      * for an object that does. Returns false otherwise. If this implements the interface then return true,
@@ -175,16 +85,8 @@
             		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
         }
     }
-    
-	public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException {
-		throw new NotYetImplementedException();
-	}
 
-	public boolean providesQueryObjectGenerator() throws SQLException {
-		return false;
-	}
-
 	protected int getJDBC4FunctionNoTableConstant() {
 		return functionNoTable;
 	}
-}
+}
\ No newline at end of file

Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java	2007-05-10 21:23:16 UTC (rev 6423)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java	2007-05-11 21:20:14 UTC (rev 6424)
@@ -27,110 +27,19 @@
 import java.sql.RowIdLifetime;
 import java.sql.SQLException;
 import java.sql.Types;
+import java.util.ArrayList;
 
-import com.mysql.jdbc.Connection;
-import com.mysql.jdbc.DatabaseMetaData;
-import com.mysql.jdbc.DatabaseMetaDataUsingInfoSchema;
-import com.mysql.jdbc.Field;
-import com.mysql.jdbc.exceptions.NotYetImplementedException;
+import java.util.List;
 
 public class JDBC4DatabaseMetaDataUsingInfoSchema extends DatabaseMetaDataUsingInfoSchema {
 	public JDBC4DatabaseMetaDataUsingInfoSchema(ConnectionImpl connToSet, String databaseToSet) {
 		super(connToSet, databaseToSet);
 	}
 
-	public boolean autoCommitFailureClosesAllResultSets() throws SQLException {
-		return false;
-	}
-
-	public ResultSet getClientInfoProperties() throws SQLException {
-		throw new NotYetImplementedException();
-	}
-
-	public ResultSet getFunctionParameters(String catalog, String schemaPattern, String functionNamePattern, String parameterNamePattern) throws SQLException {
-		throw new NotYetImplementedException();
-	}
-
-    /**
-     * Retrieves a description of the  system and user functions available 
-     * in the given catalog.
-     * <P>
-     * Only system and user function descriptions matching the schema and
-     * function name criteria are returned.  They are ordered by
-     * <code>FUNCTION_CAT</code>, <code>FUNCTION_SCHEM</code>,
-     * <code>FUNCTION_NAME</code> and 
-     * <code>SPECIFIC_ NAME</code>.
-     *
-     * <P>Each function description has the the following columns:
-     *  <OL>
-     *	<LI><B>FUNCTION_CAT</B> String => function catalog (may be <code>null</code>)
-     *	<LI><B>FUNCTION_SCHEM</B> String => function schema (may be <code>null</code>)
-     *	<LI><B>FUNCTION_NAME</B> String => function name.  This is the name 
-     * used to invoke the function
-     *	<LI><B>REMARKS</B> String => explanatory comment on the function
-     * <LI><B>FUNCTION_TYPE</B> short => kind of function:
-     *      <UL>
-     *      <LI>functionResultUnknown - Cannot determine if a return value
-     *       or table will be returned
-     *      <LI> functionNoTable- Does not return a table
-     *      <LI> functionReturnsTable - Returns a table
-     *      </UL>
-     *	<LI><B>SPECIFIC_NAME</B> String  => the name which uniquely identifies 
-     *  this function within its schema.  This is a user specified, or DBMS
-     * generated, name that may be different then the <code>FUNCTION_NAME</code> 
-     * for example with overload functions
-     *  </OL>
-     * <p>
-     * A user may not have permission to execute any of the functions that are
-     * returned by <code>getFunctions</code>
-     *
-     * @param catalog a catalog name; must match the catalog name as it
-     *        is stored in the database; "" retrieves those without a catalog;
-     *        <code>null</code> means that the catalog name should not be used to narrow
-     *        the search
-     * @param schemaPattern a schema name pattern; must match the schema name
-     *        as it is stored in the database; "" retrieves those without a schema;
-     *        <code>null</code> means that the schema name should not be used to narrow
-     *        the search
-     * @param functionNamePattern a function name pattern; must match the
-     *        function name as it is stored in the database 
-     * @return <code>ResultSet</code> - each row is a function description 
-     * @exception SQLException if a database access error occurs
-     * @see #getSearchStringEscape 
-     * @since 1.6
-     */
-	public ResultSet getFunctions(String catalog, String schemaPattern,
-			String functionNamePattern) throws SQLException {
-
-		Field[] fields = new Field[6];
-		fields[0] = new Field("", "FUNCTION_CAT", Types.CHAR,
-				MAX_IDENTIFIER_LENGTH);
-		fields[1] = new Field("", "FUNCTION_SCHEM", Types.CHAR,
-				MAX_IDENTIFIER_LENGTH);
-		fields[2] = new Field("", "FUNCTION_NAME", Types.CHAR,
-				MAX_IDENTIFIER_LENGTH);
-		fields[3] = new Field("", "REMARKS", Types.CHAR, MAX_IDENTIFIER_LENGTH);
-		fields[4] = new Field("", "FUNCTION_TYPE", Types.SMALLINT, 0);
-		fields[5] = new Field("", "SPECIFIC_NAME", Types.CHAR,
-				MAX_IDENTIFIER_LENGTH);
-
-		return getProceduresAndOrFunctions(fields, catalog, schemaPattern,
-				functionNamePattern, false, true);
-
-	}
-
 	public RowIdLifetime getRowIdLifetime() throws SQLException {
-		throw new NotYetImplementedException();
+		return RowIdLifetime.ROWID_UNSUPPORTED;
 	}
 
-	public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
-		throw new NotYetImplementedException();
-	}
-
-	public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException {
-		return true;
-	}
-
 	/**
      * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
      * for an object that does. Returns false otherwise. If this implements the interface then return true,
@@ -176,15 +85,7 @@
             		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
         }
     }
-    
-	public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException {
-		throw new NotYetImplementedException();
-	}
 
-	public boolean providesQueryObjectGenerator() throws SQLException {
-		return false;
-	}
-
 	protected int getJDBC4FunctionNoTableConstant() {
 		return functionNoTable;
 	}

Thread
Connector/J commit: r6424 - branches/branch_5_1/connector-j/src/com/mysql/jdbcmmatthews11 May