diff --git a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/Connection.scala b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/Connection.scala index c912f38b6..0bee23c24 100644 --- a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/Connection.scala +++ b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/Connection.scala @@ -60,7 +60,7 @@ object Connection: socketOptions: List[SocketOption] = defaultSocketOptions, readTimeout: Duration = Duration.Inf, allowPublicKeyRetrieval: Boolean = false, - databaseTerm: Option[DatabaseMetaData.DatabaseTerm] = None + databaseTerm: Option[DatabaseMetaData.DatabaseTerm] = Some(DatabaseMetaData.DatabaseTerm.CATALOG) ): Tracer[F] ?=> Resource[F, LdbcConnection[F]] = val logger: String => F[Unit] = s => Console[F].println(s"TLS: $s") diff --git a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/ConnectionImpl.scala b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/ConnectionImpl.scala index ce000fbd6..a3955029e 100644 --- a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/ConnectionImpl.scala +++ b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/ConnectionImpl.scala @@ -33,7 +33,7 @@ private[ldbc] case class ConnectionImpl[F[_]: Temporal: Tracer: Console: Exchang readOnly: Ref[F, Boolean], isAutoCommit: Ref[F, Boolean], connectionClosed: Ref[F, Boolean], - databaseTerm: Option[DatabaseMetaData.DatabaseTerm] = None + databaseTerm: Option[DatabaseMetaData.DatabaseTerm] = Some(DatabaseMetaData.DatabaseTerm.CATALOG) )(using ev: MonadError[F, Throwable]) extends LdbcConnection[F]: @@ -115,8 +115,12 @@ private[ldbc] case class ConnectionImpl[F[_]: Temporal: Tracer: Console: Exchang else ev.unit override def getCatalog(): F[String] = - if databaseTerm.contains(DatabaseMetaData.DatabaseTerm.CATALOG) then getSchema() - else ev.pure("") + if databaseTerm.contains(DatabaseMetaData.DatabaseTerm.CATALOG) then + for + statement <- createStatement() + result <- statement.executeQuery("SELECT DATABASE()") + yield Option(result.getString(1)).getOrElse("") + else ev.pure(null) override def setTransactionIsolation(level: Int): F[Unit] = level match @@ -484,10 +488,12 @@ private[ldbc] case class ConnectionImpl[F[_]: Temporal: Tracer: Console: Exchang override def setSchema(schema: String): F[Unit] = protocol.resetSequenceId *> protocol.comInitDB(schema) override def getSchema(): F[String] = - for - statement <- createStatement() - result <- statement.executeQuery("SELECT DATABASE()") - yield Option(result.getString(1)).getOrElse("") + if databaseTerm.contains(DatabaseMetaData.DatabaseTerm.SCHEMA) then + for + statement <- createStatement() + result <- statement.executeQuery("SELECT DATABASE()") + yield Option(result.getString(1)).getOrElse("") + else ev.pure(null) override def getStatistics: F[StatisticsPacket] = protocol.resetSequenceId *> protocol.comStatistics() diff --git a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/DatabaseMetaDataImpl.scala b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/DatabaseMetaDataImpl.scala index fc561ab79..047eb86a2 100644 --- a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/DatabaseMetaDataImpl.scala +++ b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/DatabaseMetaDataImpl.scala @@ -6,6 +6,8 @@ package ldbc.connector +import java.util.{ Locale, StringTokenizer } + import scala.collection.immutable.{ ListMap, SortedMap } import cats.* @@ -26,6 +28,7 @@ import ldbc.connector.net.packet.response.* import ldbc.connector.net.packet.request.* import ldbc.connector.net.Protocol import ldbc.connector.net.protocol.* +import ldbc.connector.util.StringHelper private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( protocol: Protocol[F], @@ -38,7 +41,8 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( getProceduresReturnsFunctions: Boolean = true, tinyInt1isBit: Boolean = true, transformedBitIsBoolean: Boolean = false, - yearIsDateType: Boolean = true + yearIsDateType: Boolean = true, + nullDatabaseMeansCurrent: Boolean = false )(using ev: MonadError[F, Throwable]) extends DatabaseMetaDataImpl.StaticDatabaseMetaData[F]: @@ -141,10 +145,10 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( override def getProcedureTerm(): String = "PROCEDURE" - override def getCatalogTerm(): String = databaseTerm.fold("") { - case DatabaseMetaData.DatabaseTerm.SCHEMA => "" - case DatabaseMetaData.DatabaseTerm.CATALOG => "CATALOG" - } + override def getCatalogTerm(): String = databaseTerm match + case Some(DatabaseMetaData.DatabaseTerm.SCHEMA) => "CATALOG" + case Some(DatabaseMetaData.DatabaseTerm.CATALOG) => "database" + case None => "database" override def supportsSchemasInDataManipulation(): Boolean = databaseTerm.fold(false) { case DatabaseMetaData.DatabaseTerm.SCHEMA => true @@ -171,10 +175,8 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( case DatabaseMetaData.DatabaseTerm.CATALOG => false } - override def supportsCatalogsInDataManipulation(): Boolean = databaseTerm.fold(false) { - case DatabaseMetaData.DatabaseTerm.SCHEMA => false - case DatabaseMetaData.DatabaseTerm.CATALOG => true - } + override def supportsCatalogsInDataManipulation(): Boolean = + databaseTerm.contains(DatabaseMetaData.DatabaseTerm.CATALOG) override def supportsCatalogsInProcedureCalls(): Boolean = databaseTerm.fold(false) { case DatabaseMetaData.DatabaseTerm.SCHEMA => false @@ -191,17 +193,14 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( case DatabaseMetaData.DatabaseTerm.CATALOG => true } - override def supportsCatalogsInPrivilegeDefinitions(): Boolean = databaseTerm.fold(false) { - case DatabaseMetaData.DatabaseTerm.SCHEMA => false - case DatabaseMetaData.DatabaseTerm.CATALOG => true - } + override def supportsCatalogsInPrivilegeDefinitions(): Boolean = + databaseTerm.contains(DatabaseMetaData.DatabaseTerm.CATALOG) override def getProcedures( catalog: Option[String], schemaPattern: Option[String], procedureNamePattern: Option[String] ): F[ResultSet] = - val db = getDatabase(catalog, schemaPattern) val sqlBuf = new StringBuilder( @@ -221,7 +220,7 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( val conditionBuf = new StringBuilder() - if getProceduresReturnsFunctions then conditionBuf.append(" ROUTINE_TYPE = 'PROCEDURE'") + if !getProceduresReturnsFunctions then conditionBuf.append(" ROUTINE_TYPE = 'PROCEDURE'") end if if db.nonEmpty then @@ -265,7 +264,6 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( procedureNamePattern: Option[String], columnNamePattern: Option[String] ): F[ResultSet] = - val db = getDatabase(catalog, schemaPattern) val supportsFractSeconds = protocol.initialPacket.serverVersion.compare(Version(5, 6, 4)) >= 0 @@ -388,7 +386,7 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( val conditionBuf = new StringBuilder() - if getProceduresReturnsFunctions then conditionBuf.append(" ROUTINE_TYPE = 'PROCEDURE'") + if !getProceduresReturnsFunctions then conditionBuf.append(" ROUTINE_TYPE = 'PROCEDURE'") end if if db.nonEmpty then @@ -476,7 +474,7 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( sqlBuf.append( if "information_schema".equalsIgnoreCase(dbValue) || "performance_schema".equalsIgnoreCase( dbValue - ) || !dbValue.contains("%") + ) || !StringHelper.hasWildcards(dbValue) || databaseTerm.contains(DatabaseMetaData.DatabaseTerm.CATALOG) then " TABLE_SCHEMA = ?" else " TABLE_SCHEMA LIKE ?" @@ -487,7 +485,7 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( case Some(tableName) => if db.nonEmpty then sqlBuf.append(" AND") end if - if tableName.contains("%") then sqlBuf.append(" TABLE_NAME LIKE ?") + if StringHelper.hasWildcards(tableName) then sqlBuf.append(" TABLE_NAME LIKE ?") else sqlBuf.append(" TABLE_NAME = ?") case None => () @@ -562,9 +560,8 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( val db = getDatabase(catalog, schemaPattern) val sqlBuf = new StringBuilder( - if databaseTerm.contains(DatabaseMetaData.DatabaseTerm.SCHEMA) then - "SELECT TABLE_CATALOG AS TABLE_CAT, TABLE_SCHEMA AS TABLE_SCHEM," - else "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM," + if databaseTerm.contains(DatabaseMetaData.DatabaseTerm.SCHEMA) then "SELECT TABLE_CATALOG, TABLE_SCHEMA," + else "SELECT TABLE_SCHEMA, NULL," ) sqlBuf.append(" TABLE_NAME, COLUMN_NAME,") @@ -687,58 +684,102 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( val conditionBuf = new StringBuilder() - db match - case Some(dbValue) => - conditionBuf.append( - if "information_schema".equalsIgnoreCase(dbValue) || "performance_schema".equalsIgnoreCase( - dbValue - ) || !dbValue.contains("%") - || databaseTerm.contains(DatabaseMetaData.DatabaseTerm.SCHEMA) - then " TABLE_SCHEMA = ?" - else " TABLE_SCHEMA LIKE ?" - ) - case None => () + db.foreach(dbValue => + conditionBuf.append( + if "information_schema".equalsIgnoreCase(dbValue) || "performance_schema".equalsIgnoreCase( + dbValue + ) || !StringHelper.hasWildcards(dbValue) + || databaseTerm.contains(DatabaseMetaData.DatabaseTerm.CATALOG) + then " TABLE_SCHEMA = ?" + else " TABLE_SCHEMA LIKE ?" + ) + ) - tableName match - case Some(tableNameValue) => - if conditionBuf.nonEmpty then conditionBuf.append(" AND") - end if - conditionBuf.append(if tableNameValue.contains("%") then " TABLE_NAME LIKE ?" else " TABLE_NAME = ?") - case None => () + tableName.foreach(name => + if conditionBuf.nonEmpty then conditionBuf.append(" AND") - columnNamePattern match - case Some(columnName) => - if conditionBuf.nonEmpty then conditionBuf.append(" AND") - end if - conditionBuf.append(if columnName.contains("%") then " COLUMN_NAME LIKE ?" else " COLUMN_NAME = ?") - case None => () + conditionBuf.append( + if StringHelper.hasWildcards(name) then " TABLE_NAME LIKE ?" + else " TABLE_NAME = ?" + ) + ) + + columnNamePattern.foreach(columnName => + if conditionBuf.nonEmpty then conditionBuf.append(" AND") + + conditionBuf.append( + if StringHelper.hasWildcards(columnName) then " COLUMN_NAME LIKE ?" + else " COLUMN_NAME = ?" + ) + ) if conditionBuf.nonEmpty then sqlBuf.append(" WHERE") end if sqlBuf.append(conditionBuf) - sqlBuf.append(" ORDER BY TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION") - prepareMetaDataSafeStatement(sqlBuf.toString()).flatMap { preparedStatement => - val settings = (db, tableName, columnNamePattern) match - case (Some(dbValue), Some(tableNameValue), Some(columnName)) => - preparedStatement.setString(1, dbValue) *> preparedStatement.setString( - 2, - tableNameValue - ) *> preparedStatement.setString(3, columnName) - case (Some(dbValue), Some(tableNameValue), None) => - preparedStatement.setString(1, dbValue) *> preparedStatement.setString(2, tableNameValue) - case (Some(dbValue), None, Some(columnName)) => - preparedStatement.setString(1, dbValue) *> preparedStatement.setString(2, columnName) - case (Some(dbValue), None, None) => preparedStatement.setString(1, dbValue) - case (None, Some(tableNameValue), Some(columnName)) => - preparedStatement.setString(1, tableNameValue) *> preparedStatement.setString(2, columnName) - case (None, Some(tableNameValue), None) => preparedStatement.setString(1, tableNameValue) - case (None, None, Some(columnName)) => preparedStatement.setString(1, columnName) - case (None, None, None) => ev.unit - - settings *> preparedStatement.executeQuery() - } + sqlBuf.append(" ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION") + + prepareMetaDataSafeStatement(sqlBuf.toString()) + .flatMap { preparedStatement => + val settings = (db, tableName, columnNamePattern) match + case (Some(dbValue), Some(tableNameValue), Some(columnName)) => + preparedStatement.setString(1, dbValue) *> preparedStatement.setString( + 2, + tableNameValue + ) *> preparedStatement.setString(3, columnName) + case (Some(dbValue), Some(tableNameValue), None) => + preparedStatement.setString(1, dbValue) *> preparedStatement.setString(2, tableNameValue) + case (Some(dbValue), None, Some(columnName)) => + preparedStatement.setString(1, dbValue) *> preparedStatement.setString(2, columnName) + case (Some(dbValue), None, None) => preparedStatement.setString(1, dbValue) + case (None, Some(tableNameValue), Some(columnName)) => + preparedStatement.setString(1, tableNameValue) *> preparedStatement.setString(2, columnName) + case (None, Some(tableNameValue), None) => preparedStatement.setString(1, tableNameValue) + case (None, None, Some(columnName)) => preparedStatement.setString(1, columnName) + case (None, None, None) => ev.unit + + settings *> preparedStatement.executeQuery() + } + .map { resultSet => + ResultSetImpl( + Vector( + "TABLE_CAT", + "TABLE_SCHEM", + "TABLE_NAME", + "COLUMN_NAME", + "DATA_TYPE", + "TYPE_NAME", + "COLUMN_SIZE", + "BUFFER_LENGTH", + "DECIMAL_DIGITS", + "NUM_PREC_RADIX", + "NULLABLE", + "REMARKS", + "COLUMN_DEF", + "SQL_DATA_TYPE", + "SQL_DATETIME_SUB", + "CHAR_OCTET_LENGTH", + "ORDINAL_POSITION", + "IS_NULLABLE", + "SCOPE_CATALOG", + "SCOPE_SCHEMA", + "SCOPE_TABLE", + "SOURCE_DATA_TYPE", + "IS_AUTOINCREMENT", + "IS_GENERATEDCOLUMN" + ).map(value => + new ColumnDefinitionPacket: + override def table: String = "" + override def name: String = value + override def columnType: ColumnDataType = ColumnDataType.MYSQL_TYPE_VARCHAR + override def flags: Seq[ColumnDefinitionFlags] = Seq.empty + ), + resultSet.asInstanceOf[ResultSetImpl].records, + serverVariables, + protocol.initialPacket.serverVersion + ) + } override def getColumnPrivileges( catalog: Option[String], @@ -794,7 +835,7 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( val db = getDatabase(catalog, schemaPattern) val sqlBuf = new StringBuilder( - "SELECT db AS TABLE_SCHEM, table_name AS TABLE_NAME, grantor AS GRANTOR, CONCAT(user, '@', host) AS GRANTEE, table_priv AS PRIVILEGE FROM mysql.tables_priv" + "SELECT host,db,table_name,grantor,user,table_priv FROM mysql.tables_priv" ) val conditionBuf = new StringBuilder() @@ -816,16 +857,89 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( sqlBuf.append(conditionBuf) end if - prepareMetaDataSafeStatement(sqlBuf.toString()).flatMap { preparedStatement => - val setting = (db, tableNamePattern) match - case (Some(dbValue), Some(tableName)) => - preparedStatement.setString(1, dbValue) *> preparedStatement.setString(2, tableName) - case (Some(dbValue), None) => preparedStatement.setString(1, dbValue) - case (None, Some(tableName)) => preparedStatement.setString(1, tableName) - case _ => ev.unit + prepareMetaDataSafeStatement(sqlBuf.toString()) + .flatMap { preparedStatement => + val setting = (db, tableNamePattern) match + case (Some(dbValue), Some(tableName)) => + preparedStatement.setString(1, dbValue) *> preparedStatement.setString(2, tableName) + case (Some(dbValue), None) => preparedStatement.setString(1, dbValue) + case (None, Some(tableName)) => preparedStatement.setString(1, tableName) + case _ => ev.unit - setting *> preparedStatement.executeQuery() - } + setting *> preparedStatement.executeQuery() + } + .flatMap { resultSet => + + val keys = Vector.newBuilder[(Option[String], Option[String], Option[String], String, String)] + while resultSet.next() do + val host = Option(resultSet.getString(1)) + val db = Option(resultSet.getString(2)) + val table = Option(resultSet.getString(3)) + val grantor = Option(resultSet.getString(4)) + val user = Option(resultSet.getString(5)).getOrElse("%") + + val fullUser = new StringBuilder(user) + host.foreach(h => fullUser.append("@").append(h)) + + Option(resultSet.getString(6)) match + case Some(value) => + val allPrivileges = value.toUpperCase(Locale.ENGLISH) + val stringTokenizer = new StringTokenizer(allPrivileges, ",") + + while stringTokenizer.hasMoreTokens do + val privilege = stringTokenizer.nextToken().trim + + keys += ((db, table, grantor, fullUser.toString(), privilege)) + end while + + case None => // no privileges + end while + + val records = keys + .result() + .traverse { (db, table, grantor, user, privilege) => + val columnResults = getColumns(catalog, schemaPattern, table, None) + columnResults.map { columnResult => + val records = Vector.newBuilder[ResultSetRowPacket] + while columnResult.next() do + val rows = Array( + if databaseTerm.contains(DatabaseMetaData.DatabaseTerm.SCHEMA) then Some("def") else db, // TABLE_CAT + if databaseTerm.contains(DatabaseMetaData.DatabaseTerm.SCHEMA) then db else None, // TABLE_SCHEM + table, // TABLE_NAME + grantor, // GRANTOR + Some(user), // GRANTEE + Some(privilege), // PRIVILEGE + None // IS_GRANTABLE + ) + records += ResultSetRowPacket(rows) + records.result() + } + } + .map(_.flatten) + + records.map { records => + ResultSetImpl( + Vector( + "TABLE_CAT", + "TABLE_SCHEM", + "TABLE_NAME", + "GRANTOR", + "GRANTEE", + "PRIVILEGE", + "IS_GRANTABLE" + ).map { value => + new ColumnDefinitionPacket: + override def table: String = "" + override def name: String = value + override def columnType: ColumnDataType = ColumnDataType.MYSQL_TYPE_VARCHAR + override def flags: Seq[ColumnDefinitionFlags] = Seq.empty + }, + records, + serverVariables, + protocol.initialPacket.serverVersion + ) + } + } override def getBestRowIdentifier( catalog: Option[String], @@ -1172,6 +1286,7 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( ResultSetRowPacket(getTypeInfo("MEDIUMBLOB")), ResultSetRowPacket(getTypeInfo("LONGBLOB")), ResultSetRowPacket(getTypeInfo("BLOB")), + ResultSetRowPacket(getTypeInfo("VECTOR")), ResultSetRowPacket(getTypeInfo("VARBINARY")), ResultSetRowPacket(getTypeInfo("TINYBLOB")), ResultSetRowPacket(getTypeInfo("BINARY")), @@ -1192,7 +1307,6 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( ResultSetRowPacket(getTypeInfo("MEDIUMINT UNSIGNED")), ResultSetRowPacket(getTypeInfo("SMALLINT")), ResultSetRowPacket(getTypeInfo("SMALLINT UNSIGNED")), - ResultSetRowPacket(getTypeInfo("YEAR")), ResultSetRowPacket(getTypeInfo("FLOAT")), ResultSetRowPacket(getTypeInfo("DOUBLE")), ResultSetRowPacket(getTypeInfo("DOUBLE PRECISION")), @@ -1203,6 +1317,7 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( ResultSetRowPacket(getTypeInfo("TINYTEXT")), ResultSetRowPacket(getTypeInfo("BOOL")), ResultSetRowPacket(getTypeInfo("DATE")), + ResultSetRowPacket(getTypeInfo("YEAR")), ResultSetRowPacket(getTypeInfo("TIME")), ResultSetRowPacket(getTypeInfo("DATETIME")), ResultSetRowPacket(getTypeInfo("TIMESTAMP")) @@ -1506,7 +1621,7 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( if tinyInt1isBit && !transformedBitIsBoolean then sqlBuf.append( - " WHEN (UPPER(DATA_TYPE)='TINYINT' AND LOCATE('ZEROFILL', UPPER(DTD_IDENTIFIER)) = 0) AND LOCATE('UNSIGNED', UPPER(DTD_IDENTIFIER)) = 0 AND LOCATE('(1)', DTD_IDENTIFIER) != 0 THEN 1" + " WHEN UPPER(DATA_TYPE)='TINYINT' AND LOCATE('ZEROFILL', UPPER(DTD_IDENTIFIER)) = 0 AND LOCATE('UNSIGNED', UPPER(DTD_IDENTIFIER)) = 0 AND LOCATE('(1)', DTD_IDENTIFIER) != 0 THEN 1" ) sqlBuf.append(" WHEN UPPER(DATA_TYPE)='MEDIUMINT' AND LOCATE('UNSIGNED', UPPER(DTD_IDENTIFIER)) != 0 THEN 8") @@ -1619,10 +1734,12 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( ) protected def getDatabase(catalog: Option[String], schema: Option[String]): Option[String] = - (databaseTerm, catalog, schema) match - case (Some(DatabaseMetaData.DatabaseTerm.SCHEMA), None, value) => value.fold(database)(_.some) - case (Some(DatabaseMetaData.DatabaseTerm.CATALOG), value, None) => value.fold(database)(_.some) - case _ => database + databaseTerm.flatMap { + case DatabaseMetaData.DatabaseTerm.SCHEMA => + if schema.nonEmpty && nullDatabaseMeansCurrent then database else schema + case DatabaseMetaData.DatabaseTerm.CATALOG => + if catalog.nonEmpty && nullDatabaseMeansCurrent then database else catalog + } /** * Get a prepared statement to query information_schema tables. @@ -1769,7 +1886,8 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( case MysqlType.TINYBLOB | MysqlType.BLOB | MysqlType.MEDIUMBLOB | MysqlType.LONGBLOB | MysqlType.TINYTEXT | MysqlType.TEXT | MysqlType.MEDIUMTEXT | MysqlType.LONGTEXT | MysqlType.JSON | MysqlType.BINARY | MysqlType.VARBINARY | MysqlType.CHAR | MysqlType.VARCHAR | MysqlType.ENUM | MysqlType.SET | MysqlType.DATE | - MysqlType.TIME | MysqlType.DATETIME | MysqlType.TIMESTAMP | MysqlType.GEOMETRY | MysqlType.UNKNOWN => + MysqlType.TIME | MysqlType.DATETIME | MysqlType.TIMESTAMP | MysqlType.GEOMETRY | MysqlType.VECTOR | + MysqlType.UNKNOWN => Array(Some("'"), Some("'")) case _ => Array(Some(""), Some("")) ) ++ Array( @@ -1782,11 +1900,13 @@ private[ldbc] case class DatabaseMetaDataImpl[F[_]: Temporal: Exchange: Tracer]( Some("false"), // FIXED_PREC_SCALE ( mysqlType match - case MysqlType.BIGINT | MysqlType.BIGINT_UNSIGNED | MysqlType.BOOLEAN | MysqlType.DOUBLE | - MysqlType.DOUBLE_UNSIGNED | MysqlType.FLOAT | MysqlType.FLOAT_UNSIGNED | MysqlType.INT | + case MysqlType.BIGINT | MysqlType.BIGINT_UNSIGNED | MysqlType.BOOLEAN | MysqlType.INT | MysqlType.INT_UNSIGNED | MysqlType.MEDIUMINT | MysqlType.MEDIUMINT_UNSIGNED | MysqlType.SMALLINT | MysqlType.SMALLINT_UNSIGNED | MysqlType.TINYINT | MysqlType.TINYINT_UNSIGNED => Some("true") + case MysqlType.DOUBLE | MysqlType.DOUBLE_UNSIGNED | MysqlType.FLOAT | MysqlType.FLOAT_UNSIGNED => + val supportsAutoIncrement = protocol.initialPacket.serverVersion.compare(Version(8, 4, 0)) >= 0 + if supportsAutoIncrement then Some("true") else Some("false") case _ => Some("false") ), // AUTO_INCREMENT Some(mysqlType.name) // LOCAL_TYPE_NAME @@ -2166,7 +2286,7 @@ private[ldbc] object DatabaseMetaDataImpl: override def getSearchStringEscape(): String = "\\" - override def getExtraNameCharacters(): String = "#@" + override def getExtraNameCharacters(): String = "$" override def supportsAlterTableWithAddColumn(): Boolean = true @@ -2320,32 +2440,5 @@ private[ldbc] object DatabaseMetaDataImpl: override def supportsResultSetHoldability(holdability: Int): Boolean = holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT - override def locatorsUpdateCopy(): Boolean = false + override def locatorsUpdateCopy(): Boolean = true end StaticDatabaseMetaData - - def apply[F[_]: Temporal: Exchange: Tracer]( - protocol: Protocol[F], - serverVariables: Map[String, String], - connectionClosed: Ref[F, Boolean], - statementClosed: Ref[F, Boolean], - resultSetClosed: Ref[F, Boolean], - database: Option[String] = None, - databaseTerm: Option[DatabaseMetaData.DatabaseTerm] = None, - getProceduresReturnsFunctions: Boolean = true, - tinyInt1isBit: Boolean = true, - transformedBitIsBoolean: Boolean = false, - yearIsDateType: Boolean = true - )(using ev: MonadError[F, Throwable]): DatabaseMetaData[F] = - new DatabaseMetaDataImpl[F]( - protocol, - serverVariables, - connectionClosed, - statementClosed, - resultSetClosed, - database, - databaseTerm, - getProceduresReturnsFunctions, - tinyInt1isBit, - transformedBitIsBoolean, - yearIsDateType - ) diff --git a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/data/MysqlType.scala b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/data/MysqlType.scala index 0b3ce7b1e..32af47969 100644 --- a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/data/MysqlType.scala +++ b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/data/MysqlType.scala @@ -610,6 +610,15 @@ enum MysqlType( */ case GEOMETRY extends MysqlType("GEOMETRY", Types.BINARY, 0, MysqlTypeVariables.IS_NOT_DECIMAL, 65535L, "") + /** + * VECTOR[(M)] + * A VECTOR column with a maximum length of 65,532 (16383 x 4) bytes. An optional length M can be given for this type to indicate the maximum number of + * entries in a VECTOR. M cannot exceed 16383. Each entry is a 4 Byte (single-precision) floating-point value. + * + * Protocol: FIELD_TYPE_VECTOR = 242 + */ + case VECTOR extends MysqlType("VECTOR", Types.LONGVARBINARY, 0, MysqlTypeVariables.IS_NOT_DECIMAL, 65532L, "[(M)]") + /** * Fall-back type for those MySQL data types which c/J can't recognize. * Handled the same as BLOB. @@ -689,6 +698,7 @@ object MysqlType: case name if name.contains("BLOB") => MysqlType.BLOB case name if name.contains("TEXT") => MysqlType.TEXT case name if name.contains("GEOM") | name.contains("POINT") | name.contains("POLYGON") => MysqlType.GEOMETRY + case name if name.contains("VECTOR") => MysqlType.VECTOR case _ => MysqlType.UNKNOWN /** diff --git a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/util/StringHelper.scala b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/util/StringHelper.scala index 830776891..2e1859da6 100644 --- a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/util/StringHelper.scala +++ b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/util/StringHelper.scala @@ -71,3 +71,14 @@ object StringHelper: def getUniqueSavepointId: String = val uuid = UUID.randomUUID().toString uuid.replaceAll("-", "_") + + /** + * Does the string contain wildcard symbols ('%' or '_'). Used in DatabaseMetaData. + * + * @param src + * string + * @return + * true if src contains wildcard symbols + */ + def hasWildcards(src: String): Boolean = + indexOfIgnoreCase(0, src, "%") != -1 || indexOfIgnoreCase(0, src, "_") != -1 diff --git a/module/ldbc-connector/shared/src/test/scala/ldbc/connector/ConnectionTest.scala b/module/ldbc-connector/shared/src/test/scala/ldbc/connector/ConnectionTest.scala index 5ed31c42e..1b49e5340 100644 --- a/module/ldbc-connector/shared/src/test/scala/ldbc/connector/ConnectionTest.scala +++ b/module/ldbc-connector/shared/src/test/scala/ldbc/connector/ConnectionTest.scala @@ -264,7 +264,7 @@ class ConnectionTest extends FTestPlatform: assertIOBoolean(connection.use(_ => IO(true))) } - test("Schema change will change the currently connected Schema.") { + test("Catalog change will change the currently connected Catalog.") { val connection = Connection[IO]( host = "127.0.0.1", port = 13306, @@ -276,7 +276,7 @@ class ConnectionTest extends FTestPlatform: assertIO( connection.use { conn => - conn.setSchema("world") *> conn.getSchema() + conn.setCatalog("world") *> conn.getCatalog() }, "world" ) @@ -884,7 +884,7 @@ class ConnectionTest extends FTestPlatform: assertIO( connection.use(_.getMetaData().map(_.getExtraNameCharacters())), - "#@" + "$" ) } @@ -902,7 +902,7 @@ class ConnectionTest extends FTestPlatform: connection.use { conn => for metaData <- conn.getMetaData() - resultSet <- metaData.getProcedures("def", "connector_test", "demoSp") + resultSet <- metaData.getProcedures(Some("connector_test"), None, Some("demoSp")) yield val builder = Vector.newBuilder[String] while resultSet.next() do @@ -934,7 +934,7 @@ class ConnectionTest extends FTestPlatform: connection.use { conn => for metaData <- conn.getMetaData() - resultSet <- metaData.getProcedureColumns(Some("def"), Some("connector_test"), Some("demoSp"), None) + resultSet <- metaData.getProcedureColumns(Some("connector_test"), None, Some("demoSp"), None) yield val builder = Vector.newBuilder[String] while resultSet.next() do @@ -967,7 +967,7 @@ class ConnectionTest extends FTestPlatform: connection.use { conn => for metaData <- conn.getMetaData() - resultSet <- metaData.getTables(None, Some("connector_test"), Some("all_types"), Array.empty[String]) + resultSet <- metaData.getTables(Some("connector_test"), None, Some("all_types"), Array.empty[String]) yield val builder = Vector.newBuilder[String] while resultSet.next() do @@ -1185,13 +1185,12 @@ class ConnectionTest extends FTestPlatform: test("The result of retrieving table privileges information matches the specified value.") { val connection = Connection[IO]( - host = "127.0.0.1", - port = 13306, - user = "ldbc", - password = Some("password"), - database = Some("connector_test"), - ssl = SSL.Trusted, - databaseTerm = Some(DatabaseMetaData.DatabaseTerm.SCHEMA) + host = "127.0.0.1", + port = 13306, + user = "ldbc", + password = Some("password"), + database = Some("connector_test"), + ssl = SSL.Trusted ) assertIO( @@ -1213,7 +1212,12 @@ class ConnectionTest extends FTestPlatform: builder.result() }, Vector( - "Table Cat: null, Table Schem: connector_test, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: Select,Insert, Is Grantable: null" + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: SELECT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: SELECT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: SELECT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: INSERT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: INSERT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: INSERT, Is Grantable: null" ) ) } @@ -1499,6 +1503,7 @@ class ConnectionTest extends FTestPlatform: "Type Name: MEDIUMBLOB, Data Type: -4, Precision: 16777215, Literal Prefix: ', Literal Suffix: ', Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: MEDIUMBLOB, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: LONGBLOB, Data Type: -4, Precision: 2147483647, Literal Prefix: ', Literal Suffix: ', Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: LONGBLOB, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: BLOB, Data Type: -4, Precision: 65535, Literal Prefix: ', Literal Suffix: ', Create Params: [(M)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: BLOB, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: VECTOR, Data Type: -4, Precision: 65532, Literal Prefix: ', Literal Suffix: ', Create Params: [(M)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: VECTOR, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: VARBINARY, Data Type: -3, Precision: 65535, Literal Prefix: ', Literal Suffix: ', Create Params: (M), Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: VARBINARY, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: TINYBLOB, Data Type: -3, Precision: 255, Literal Prefix: ', Literal Suffix: ', Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: TINYBLOB, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: BINARY, Data Type: -2, Precision: 255, Literal Prefix: ', Literal Suffix: ', Create Params: (M), Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: BINARY, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", @@ -1519,17 +1524,17 @@ class ConnectionTest extends FTestPlatform: "Type Name: MEDIUMINT UNSIGNED, Data Type: 4, Precision: 8, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: MEDIUMINT UNSIGNED, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: SMALLINT, Data Type: 5, Precision: 5, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: SMALLINT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: SMALLINT UNSIGNED, Data Type: 5, Precision: 5, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: SMALLINT UNSIGNED, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", - "Type Name: YEAR, Data Type: 91, Precision: 4, Literal Prefix: , Literal Suffix: , Create Params: [(4)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: YEAR, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", - "Type Name: FLOAT, Data Type: 7, Precision: 12, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: FLOAT, Minimum Scale: -38, Maximum Scale: 38, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", - "Type Name: DOUBLE, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: DOUBLE, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", - "Type Name: DOUBLE PRECISION, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: DOUBLE, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", - "Type Name: REAL, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: DOUBLE, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", - "Type Name: DOUBLE UNSIGNED, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: DOUBLE UNSIGNED, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", - "Type Name: DOUBLE PRECISION UNSIGNED, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: DOUBLE UNSIGNED, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: FLOAT, Data Type: 7, Precision: 12, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: FLOAT, Minimum Scale: -38, Maximum Scale: 38, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DOUBLE, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DOUBLE PRECISION, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: REAL, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DOUBLE UNSIGNED, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE UNSIGNED, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DOUBLE PRECISION UNSIGNED, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE UNSIGNED, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: VARCHAR, Data Type: 12, Precision: 65535, Literal Prefix: ', Literal Suffix: ', Create Params: (M) [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: VARCHAR, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: TINYTEXT, Data Type: 12, Precision: 255, Literal Prefix: ', Literal Suffix: ', Create Params: [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: TINYTEXT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: BOOL, Data Type: 16, Precision: 3, Literal Prefix: , Literal Suffix: , Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: BOOLEAN, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: DATE, Data Type: 91, Precision: 10, Literal Prefix: ', Literal Suffix: ', Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DATE, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: YEAR, Data Type: 91, Precision: 4, Literal Prefix: , Literal Suffix: , Create Params: [(4)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: YEAR, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: TIME, Data Type: 92, Precision: 16, Literal Prefix: ', Literal Suffix: ', Create Params: [(fsp)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: TIME, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: DATETIME, Data Type: 93, Precision: 26, Literal Prefix: ', Literal Suffix: ', Create Params: [(fsp)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DATETIME, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", "Type Name: TIMESTAMP, Data Type: 93, Precision: 26, Literal Prefix: ', Literal Suffix: ', Create Params: [(fsp)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: TIMESTAMP, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10" diff --git a/module/ldbc-sql/src/main/scala/ldbc/sql/DatabaseMetaData.scala b/module/ldbc-sql/src/main/scala/ldbc/sql/DatabaseMetaData.scala index d6f26e7b9..05c346ae8 100644 --- a/module/ldbc-sql/src/main/scala/ldbc/sql/DatabaseMetaData.scala +++ b/module/ldbc-sql/src/main/scala/ldbc/sql/DatabaseMetaData.scala @@ -1064,7 +1064,7 @@ trait DatabaseMetaData[F[_]]: * Only procedure descriptions matching the schema and * procedure name criteria are returned. They are ordered by * PROCEDURE_CAT, PROCEDURE_SCHEM, - * PROCEDURE_NAME and SPECIFIC_ NAME. + * PROCEDURE_NAME and SPECIFIC_NAME. * *

Each procedure description has the following columns: *

    diff --git a/tests/src/test/scala/ldbc/tests/ConnectionTest.scala b/tests/src/test/scala/ldbc/tests/ConnectionTest.scala new file mode 100644 index 000000000..9bb8139c3 --- /dev/null +++ b/tests/src/test/scala/ldbc/tests/ConnectionTest.scala @@ -0,0 +1,883 @@ +/** + * Copyright (c) 2023-2024 by Takahiko Tominaga + * This software is licensed under the MIT License (MIT). + * For more information see LICENSE or https://opensource.org/licenses/MIT + */ + +package ldbc.tests + +import com.mysql.cj.jdbc.MysqlDataSource + +import cats.effect.* + +import org.typelevel.otel4s.trace.Tracer + +import munit.* + +import ldbc.sql.* +import ldbc.connector.SSL + +class LdbcConnectionTest extends ConnectionTest: + override def prefix: "ldbc" = "ldbc" + + override def connection(databaseTerm: "SCHEMA" | "CATALOG" = "CATALOG"): Resource[IO, Connection[IO]] = + ldbc.connector.Connection[IO]( + host = host, + port = port, + user = user, + password = Some(password), + database = Some(database), + ssl = SSL.Trusted, + databaseTerm = Some(databaseTerm match + case "SCHEMA" => DatabaseMetaData.DatabaseTerm.SCHEMA + case "CATALOG" => DatabaseMetaData.DatabaseTerm.CATALOG + ) + ) + +class JdbcConnectionTest extends ConnectionTest: + + val ds = new MysqlDataSource() + ds.setServerName(host) + ds.setPortNumber(port) + ds.setDatabaseName(database) + ds.setUser(user) + ds.setPassword(password) + + override def prefix: "jdbc" | "ldbc" = "jdbc" + + override def connection(databaseTerm: "SCHEMA" | "CATALOG" = "CATALOG"): Resource[IO, Connection[IO]] = + ds.setDatabaseTerm(databaseTerm) + Resource.make(jdbc.connector.MysqlDataSource[IO](ds).getConnection)(_.close()) + +trait ConnectionTest extends CatsEffectSuite: + + given Tracer[IO] = Tracer.noop[IO] + + protected val host: String = "127.0.0.1" + protected val port: Int = 13306 + protected val user: String = "ldbc" + protected val password: String = "password" + protected val database: String = "connector_test" + + def prefix: "jdbc" | "ldbc" + def connection(databaseTerm: "SCHEMA" | "CATALOG" = "CATALOG"): Resource[IO, Connection[IO]] + + test("Catalog change will change the currently connected Catalog.") { + assertIO( + connection().use { conn => + conn.setCatalog("world") *> conn.getCatalog() + }, + "world" + ) + } + + test("The connection is valid.") { + assertIOBoolean(connection().use(_.isValid(0))) + } + + test("The allProceduresAreCallable method of DatabaseMetaData is always false.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.allProceduresAreCallable()))) + } + + test("The allTablesAreSelectable method of DatabaseMetaData is always false.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.allTablesAreSelectable()))) + } + + test("The URL retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getURL())), + "jdbc:mysql://127.0.0.1:13306/connector_test" + ) + } + + test("The User name retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().flatMap(_.getUserName())), + "ldbc@172.18.0.1" + ) + } + + test("The isReadOnly method of DatabaseMetaData is always false.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.isReadOnly()))) + } + + test("The nullsAreSortedHigh method of DatabaseMetaData is always false.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.nullsAreSortedHigh()))) + } + + test("The nullsAreSortedLow method of DatabaseMetaData is always true.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => meta.nullsAreSortedLow()))) + } + + test("The nullsAreSortedAtStart method of DatabaseMetaData is always false.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.nullsAreSortedAtStart()))) + } + + test("The nullsAreSortedAtEnd method of DatabaseMetaData is always false.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.nullsAreSortedAtEnd()))) + } + + test("The getDatabaseProductName method of DatabaseMetaData is always MySQL.") { + assertIO( + connection().use(_.getMetaData().map(_.getDatabaseProductName())), + "MySQL" + ) + } + + test("The Server version retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getDatabaseProductVersion())), + "8.4.0" + ) + } + + test("The getDriverName method of DatabaseMetaData is always MySQL Connector/L.") { + assertIO( + connection().use(_.getMetaData().map(_.getDriverName())), + if prefix == "jdbc" then "MySQL Connector/J" else "MySQL Connector/L" + ) + } + + test("The Driver version retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getDriverVersion())), + if prefix == "jdbc" then "mysql-connector-j-8.4.0 (Revision: 1c3f5c149e0bfe31c7fbeb24e2d260cd890972c4)" + else "ldbc-connector-0.3.0" + ) + } + + test("The usesLocalFiles method of DatabaseMetaData is always false.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.usesLocalFiles()))) + } + + test("The usesLocalFilePerTable method of DatabaseMetaData is always false.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.usesLocalFilePerTable()))) + } + + test("The supports Mixed Case Identifiers retrieved from DatabaseMetaData matches the specified value.") { + assertIOBoolean(connection().use(_.getMetaData().map(_.supportsMixedCaseIdentifiers()))) + } + + test("The storesUpperCaseIdentifiers method of DatabaseMetaData is always false.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.storesUpperCaseIdentifiers()))) + } + + test("The stores Lower Case Identifiers retrieved from DatabaseMetaData matches the specified value.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.storesLowerCaseIdentifiers()))) + } + + test("The stores Mixed Case Identifiers retrieved from DatabaseMetaData matches the specified value.") { + assertIOBoolean(connection().use(_.getMetaData().map(_.storesMixedCaseIdentifiers()))) + } + + test("The supports Mixed Case Quoted Identifiers retrieved from DatabaseMetaData matches the specified value.") { + assertIOBoolean(connection().use(_.getMetaData().map(_.supportsMixedCaseQuotedIdentifiers()))) + } + + test("The storesUpperCaseQuotedIdentifiers method of DatabaseMetaData is always true.") { + assertIOBoolean(connection().use(_.getMetaData().map(_.storesUpperCaseQuotedIdentifiers()))) + } + + test("The stores Lower Case Quoted Identifiers retrieved from DatabaseMetaData matches the specified value.") { + assertIOBoolean(connection().use(_.getMetaData().map(meta => !meta.storesLowerCaseQuotedIdentifiers()))) + } + + test("The stores Mixed Case Quoted Identifiers retrieved from DatabaseMetaData matches the specified value.") { + assertIOBoolean(connection().use(_.getMetaData().map(_.storesMixedCaseQuotedIdentifiers()))) + } + + test("The Identifier Quote String retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getIdentifierQuoteString())), + "`" + ) + } + + test("The SQL Keywords retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().flatMap(_.getSQLKeywords())), + "ACCESSIBLE,ADD,ANALYZE,ASC,BEFORE,CASCADE,CHANGE,CONTINUE,DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,DESC,DISTINCTROW,DIV,DUAL,ELSEIF,EMPTY,ENCLOSED,ESCAPED,EXIT,EXPLAIN,FIRST_VALUE,FLOAT4,FLOAT8,FORCE,FULLTEXT,GENERATED,GROUPS,HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,INDEX,INFILE,INT1,INT2,INT3,INT4,INT8,IO_AFTER_GTIDS,IO_BEFORE_GTIDS,ITERATE,JSON_TABLE,KEY,KEYS,KILL,LAG,LAST_VALUE,LEAD,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY,MAXVALUE,MEDIUMBLOB,MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND,NO_WRITE_TO_BINLOG,NTH_VALUE,NTILE,OPTIMIZE,OPTIMIZER_COSTS,OPTION,OPTIONALLY,OUTFILE,PURGE,READ,READ_WRITE,REGEXP,RENAME,REPEAT,REPLACE,REQUIRE,RESIGNAL,RESTRICT,RLIKE,SCHEMA,SCHEMAS,SECOND_MICROSECOND,SEPARATOR,SHOW,SIGNAL,SPATIAL,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT,SSL,STARTING,STORED,STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT,TINYTEXT,UNDO,UNLOCK,UNSIGNED,USAGE,USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,VARCHARACTER,VIRTUAL,WHILE,WRITE,XOR,YEAR_MONTH,ZEROFILL" + ) + } + + test("The Numeric Functions retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getNumericFunctions())), + "ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,COT,DEGREES,EXP,FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW,POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE" + ) + } + + test("The String Functions retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getStringFunctions())), + "ASCII,BIN,BIT_LENGTH,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,CONCAT_WS,CONV,ELT,EXPORT_SET,FIELD,FIND_IN_SET,HEX,INSERT," + + "INSTR,LCASE,LEFT,LENGTH,LOAD_FILE,LOCATE,LOCATE,LOWER,LPAD,LTRIM,MAKE_SET,MATCH,MID,OCT,OCTET_LENGTH,ORD,POSITION," + + "QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,SPACE,STRCMP,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING," + + "SUBSTRING_INDEX,TRIM,UCASE,UPPER" + ) + } + + test("The System Functions retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getSystemFunctions())), + "DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION" + ) + } + + test("The Time Date Functions retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getTimeDateFunctions())), + "DAYOFWEEK,WEEKDAY,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME,MONTHNAME,QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,PERIOD_ADD," + + "PERIOD_DIFF,TO_DAYS,FROM_DAYS,DATE_FORMAT,TIME_FORMAT,CURDATE,CURRENT_DATE,CURTIME,CURRENT_TIME,NOW,SYSDATE," + + "CURRENT_TIMESTAMP,UNIX_TIMESTAMP,FROM_UNIXTIME,SEC_TO_TIME,TIME_TO_SEC" + ) + } + + test("The Search String Escape retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getSearchStringEscape())), + "\\" + ) + } + + test("The Extra Name Characters retrieved from DatabaseMetaData matches the specified value.") { + assertIO( + connection().use(_.getMetaData().map(_.getExtraNameCharacters())), + "$" + ) + } + + test("The result of retrieving procedure information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getProcedures(Some("connector_test"), None, Some("demoSp")) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val procedureCat = resultSet.getString("PROCEDURE_CAT") + val procedureSchem = resultSet.getString("PROCEDURE_SCHEM") + val procedureName = resultSet.getString("PROCEDURE_NAME") + val remarks = resultSet.getString("REMARKS") + val procedureType = resultSet.getString("PROCEDURE_TYPE") + builder += s"Procedure Catalog: $procedureCat, Procedure Schema: $procedureSchem, Procedure Name: $procedureName, Remarks: $remarks, Procedure Type: $procedureType" + builder.result() + }, + Vector( + "Procedure Catalog: connector_test, Procedure Schema: null, Procedure Name: demoSp, Remarks: , Procedure Type: 1" + ) + ) + } + + test("The result of retrieving procedure columns information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getProcedureColumns(Some("connector_test"), None, Some("demoSp"), None) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val procedureCat = resultSet.getString("PROCEDURE_CAT") + val procedureSchem = resultSet.getString("PROCEDURE_SCHEM") + val procedureName = resultSet.getString("PROCEDURE_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val columnType = resultSet.getString("COLUMN_TYPE") + builder += s"Procedure Catalog: $procedureCat, Procedure Schema: $procedureSchem, Procedure Name: $procedureName, Column Name: $columnName, Column Type: $columnType" + builder.result() + }, + Vector( + "Procedure Catalog: connector_test, Procedure Schema: null, Procedure Name: demoSp, Column Name: inputParam, Column Type: 1", + "Procedure Catalog: connector_test, Procedure Schema: null, Procedure Name: demoSp, Column Name: inOutParam, Column Type: 2" + ) + ) + } + + test("The result of retrieving tables information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getTables(Some("connector_test"), None, Some("all_types"), Array.empty[String]) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val tableType = resultSet.getString("TABLE_TYPE") + val remarks = resultSet.getString("REMARKS") + val typeCat = resultSet.getString("TYPE_CAT") + val typeSchem = resultSet.getString("TYPE_SCHEM") + val typeName = resultSet.getString("TYPE_NAME") + val selfReferencingColName = resultSet.getString("SELF_REFERENCING_COL_NAME") + val refGeneration = resultSet.getString("REF_GENERATION") + builder += s"Table Catalog: $tableCat, Table Schema: $tableSchem, Table Name: $tableName, Table Type: $tableType, Remarks: $remarks, Type Catalog: $typeCat, Type Schema: $typeSchem, Type Name: $typeName, Self Referencing Column Name: $selfReferencingColName, Reference Generation: $refGeneration" + builder.result() + }, + Vector( + "Table Catalog: connector_test, Table Schema: null, Table Name: all_types, Table Type: TABLE, Remarks: , Type Catalog: null, Type Schema: null, Type Name: null, Self Referencing Column Name: null, Reference Generation: null" + ) + ) + } + + test("The result of retrieving schemas information matches the specified value.") { + assertIO( + connection("SCHEMA").use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getSchemas() + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCatalog = resultSet.getString("TABLE_CATALOG") + val tableSchem = resultSet.getString("TABLE_SCHEM") + builder += s"Table Catalog: $tableCatalog, Table Schema: $tableSchem" + builder.result() + }, + Vector( + "Table Catalog: def, Table Schema: benchmark", + "Table Catalog: def, Table Schema: connector_test", + "Table Catalog: def, Table Schema: information_schema", + "Table Catalog: def, Table Schema: mysql", + "Table Catalog: def, Table Schema: performance_schema", + "Table Catalog: def, Table Schema: sys", + "Table Catalog: def, Table Schema: world", + "Table Catalog: def, Table Schema: world2" + ) + ) + } + + test("The result of retrieving catalogs information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getCatalogs() + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do builder += s"Table Catalog: ${ resultSet.getString("TABLE_CAT") }" + builder.result() + }, + Vector( + "Table Catalog: benchmark", + "Table Catalog: connector_test", + "Table Catalog: information_schema", + "Table Catalog: mysql", + "Table Catalog: performance_schema", + "Table Catalog: sys", + "Table Catalog: world", + "Table Catalog: world2" + ) + ) + } + + test("The result of retrieving tableTypes information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet = metaData.getTableTypes() + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do builder += s"Table Type: ${ resultSet.getString("TABLE_TYPE") }" + builder.result() + }, + Vector( + "Table Type: LOCAL TEMPORARY", + "Table Type: SYSTEM TABLE", + "Table Type: SYSTEM VIEW", + "Table Type: TABLE", + "Table Type: VIEW" + ) + ) + } + + test("The result of retrieving column privileges information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getColumnPrivileges(Some("connector_test"), None, Some("privileges_table"), None) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val grantor = resultSet.getString("GRANTOR") + val grantee = resultSet.getString("GRANTEE") + val privilege = resultSet.getString("PRIVILEGE") + val isGrantable = resultSet.getString("IS_GRANTABLE") + builder += s"Table Cat: $tableCat, Table Schem: $tableSchem, Table Name: $tableName, Column Name: $columnName, Grantor: $grantor, Grantee: $grantee, Privilege: $privilege, Is Grantable: $isGrantable" + builder.result() + }, + Vector( + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Column Name: c1, Grantor: null, Grantee: 'ldbc'@'%', Privilege: INSERT, Is Grantable: NO", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Column Name: c1, Grantor: null, Grantee: 'ldbc'@'%', Privilege: SELECT, Is Grantable: NO", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Column Name: c2, Grantor: null, Grantee: 'ldbc'@'%', Privilege: INSERT, Is Grantable: NO", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Column Name: c2, Grantor: null, Grantee: 'ldbc'@'%', Privilege: SELECT, Is Grantable: NO" + ) + ) + } + + test("The result of retrieving table privileges information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getTablePrivileges(None, None, Some("privileges_table")) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val grantor = resultSet.getString("GRANTOR") + val grantee = resultSet.getString("GRANTEE") + val privilege = resultSet.getString("PRIVILEGE") + val isGrantable = resultSet.getString("IS_GRANTABLE") + builder += s"Table Cat: $tableCat, Table Schem: $tableSchem, Table Name: $tableName, Grantor: $grantor, Grantee: $grantee, Privilege: $privilege, Is Grantable: $isGrantable" + builder.result() + }, + Vector( + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: SELECT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: SELECT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: SELECT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: INSERT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: INSERT, Is Grantable: null", + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Grantor: root@localhost, Grantee: ldbc@%, Privilege: INSERT, Is Grantable: null" + ) + ) + } + + test("The result of retrieving best row identifier information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getBestRowIdentifier(None, Some("connector_test"), "privileges_table", None, None) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val scope = resultSet.getShort("SCOPE") + val columnName = resultSet.getString("COLUMN_NAME") + val dataType = resultSet.getInt("DATA_TYPE") + val typeName = resultSet.getString("TYPE_NAME") + val columnSize = resultSet.getInt("COLUMN_SIZE") + val bufferLength = resultSet.getInt("BUFFER_LENGTH") + val decimalDigits = resultSet.getShort("DECIMAL_DIGITS") + val pseudoColumn = resultSet.getShort("PSEUDO_COLUMN") + builder += s"Scope: $scope, Column Name: $columnName, Data Type: $dataType, Type Name: $typeName, Column Size: $columnSize, Buffer Length: $bufferLength, Decimal Digits: $decimalDigits, Pseudo Column: $pseudoColumn" + builder.result() + }, + Vector( + "Scope: 2, Column Name: c1, Data Type: 4, Type Name: int, Column Size: 10, Buffer Length: 65535, Decimal Digits: 0, Pseudo Column: 1" + ) + ) + } + + test("The result of retrieving version columns information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getVersionColumns(None, Some("connector_test"), "privileges_table") + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val scope = resultSet.getShort("SCOPE") + val columnName = resultSet.getString("COLUMN_NAME") + val dataType = resultSet.getInt("DATA_TYPE") + val typeName = resultSet.getString("TYPE_NAME") + val columnSize = resultSet.getInt("COLUMN_SIZE") + val bufferLength = resultSet.getInt("BUFFER_LENGTH") + val decimalDigits = resultSet.getShort("DECIMAL_DIGITS") + val pseudoColumn = resultSet.getShort("PSEUDO_COLUMN") + builder += s"Scope: $scope, Column Name: $columnName, Data Type: $dataType, Type Name: $typeName, Column Size: $columnSize, Buffer Length: $bufferLength, Decimal Digits: $decimalDigits, Pseudo Column: $pseudoColumn" + builder.result() + }, + Vector( + "Scope: 0, Column Name: updated_at, Data Type: 93, Type Name: TIMESTAMP, Column Size: 19, Buffer Length: 65535, Decimal Digits: 0, Pseudo Column: 1" + ) + ) + } + + test("The result of retrieving primary key information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getPrimaryKeys(Some("connector_test"), None, "privileges_table") + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val keySeq = resultSet.getShort("KEY_SEQ") + val pkName = resultSet.getString("PK_NAME") + builder += s"Table Cat: $tableCat, Table Schem: $tableSchem, Table Name: $tableName, Column Name: $columnName, Key Seq: $keySeq, PK Name: $pkName" + builder.result() + }, + Vector( + "Table Cat: connector_test, Table Schem: null, Table Name: privileges_table, Column Name: c1, Key Seq: 1, PK Name: PRIMARY" + ) + ) + } + + test("The result of retrieving imported key information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getImportedKeys(Some("world"), None, "city") + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val pktableCat = resultSet.getString("PKTABLE_CAT") + val pktableSchem = resultSet.getString("PKTABLE_SCHEM") + val pktableName = resultSet.getString("PKTABLE_NAME") + val pkcolumnName = resultSet.getString("PKCOLUMN_NAME") + val fktableCat = resultSet.getString("FKTABLE_CAT") + val fktableSchem = resultSet.getString("FKTABLE_SCHEM") + val fktableName = resultSet.getString("FKTABLE_NAME") + val fkcolumnName = resultSet.getString("FKCOLUMN_NAME") + val keySeq = resultSet.getShort("KEY_SEQ") + val updateRule = resultSet.getShort("UPDATE_RULE") + val deleteRule = resultSet.getShort("DELETE_RULE") + val fkName = resultSet.getString("FK_NAME") + val pkName = resultSet.getString("PK_NAME") + val deferrability = resultSet.getShort("DEFERRABILITY") + builder += s"PK Table Cat: $pktableCat, PK Table Schem: $pktableSchem, PK Table Name: $pktableName, PK Column Name: $pkcolumnName, FK Table Cat: $fktableCat, FK Table Schem: $fktableSchem, FK Table Name: $fktableName, FK Column Name: $fkcolumnName, Key Seq: $keySeq, Update Rule: $updateRule, Delete Rule: $deleteRule, FK Name: $fkName, PK Name: $pkName, Deferrability: $deferrability" + builder.result() + }, + Vector( + "PK Table Cat: world, PK Table Schem: null, PK Table Name: country, PK Column Name: Code, FK Table Cat: world, FK Table Schem: null, FK Table Name: city, FK Column Name: CountryCode, Key Seq: 1, Update Rule: 1, Delete Rule: 1, FK Name: city_ibfk_1, PK Name: PRIMARY, Deferrability: 7" + ) + ) + } + + test("The result of retrieving exported key information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getExportedKeys(Some("world"), None, "city") + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val pktableCat = resultSet.getString("PKTABLE_CAT") + val pktableSchem = resultSet.getString("PKTABLE_SCHEM") + val pktableName = resultSet.getString("PKTABLE_NAME") + val pkcolumnName = resultSet.getString("PKCOLUMN_NAME") + val fktableCat = resultSet.getString("FKTABLE_CAT") + val fktableSchem = resultSet.getString("FKTABLE_SCHEM") + val fktableName = resultSet.getString("FKTABLE_NAME") + val fkcolumnName = resultSet.getString("FKCOLUMN_NAME") + val keySeq = resultSet.getShort("KEY_SEQ") + val updateRule = resultSet.getShort("UPDATE_RULE") + val deleteRule = resultSet.getShort("DELETE_RULE") + val fkName = resultSet.getString("FK_NAME") + val pkName = resultSet.getString("PK_NAME") + val deferrability = resultSet.getShort("DEFERRABILITY") + builder += s"PK Table Cat: $pktableCat, PK Table Schem: $pktableSchem, PK Table Name: $pktableName, PK Column Name: $pkcolumnName, FK Table Cat: $fktableCat, FK Table Schem: $fktableSchem, FK Table Name: $fktableName, FK Column Name: $fkcolumnName, Key Seq: $keySeq, Update Rule: $updateRule, Delete Rule: $deleteRule, FK Name: $fkName, PK Name: $pkName, Deferrability: $deferrability" + builder.result() + }, + Vector( + "PK Table Cat: world, PK Table Schem: null, PK Table Name: city, PK Column Name: ID, FK Table Cat: world, FK Table Schem: null, FK Table Name: government_office, FK Column Name: CityID, Key Seq: 1, Update Rule: 1, Delete Rule: 1, FK Name: government_office_ibfk_1, PK Name: PRIMARY, Deferrability: 7" + ) + ) + } + + test("The result of retrieving cross reference information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- + metaData.getCrossReference(Some("world"), None, "city", Some("world"), None, Some("government_office")) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val pktableCat = resultSet.getString("PKTABLE_CAT") + val pktableSchem = resultSet.getString("PKTABLE_SCHEM") + val pktableName = resultSet.getString("PKTABLE_NAME") + val pkcolumnName = resultSet.getString("PKCOLUMN_NAME") + val fktableCat = resultSet.getString("FKTABLE_CAT") + val fktableSchem = resultSet.getString("FKTABLE_SCHEM") + val fktableName = resultSet.getString("FKTABLE_NAME") + val fkcolumnName = resultSet.getString("FKCOLUMN_NAME") + val keySeq = resultSet.getShort("KEY_SEQ") + val updateRule = resultSet.getShort("UPDATE_RULE") + val deleteRule = resultSet.getShort("DELETE_RULE") + val fkName = resultSet.getString("FK_NAME") + val pkName = resultSet.getString("PK_NAME") + val deferrability = resultSet.getShort("DEFERRABILITY") + builder += s"PK Table Cat: $pktableCat, PK Table Schem: $pktableSchem, PK Table Name: $pktableName, PK Column Name: $pkcolumnName, FK Table Cat: $fktableCat, FK Table Schem: $fktableSchem, FK Table Name: $fktableName, FK Column Name: $fkcolumnName, Key Seq: $keySeq, Update Rule: $updateRule, Delete Rule: $deleteRule, FK Name: $fkName, PK Name: $pkName, Deferrability: $deferrability" + builder.result() + }, + Vector( + "PK Table Cat: world, PK Table Schem: null, PK Table Name: city, PK Column Name: ID, FK Table Cat: world, FK Table Schem: null, FK Table Name: government_office, FK Column Name: CityID, Key Seq: 1, Update Rule: 1, Delete Rule: 1, FK Name: government_office_ibfk_1, PK Name: PRIMARY, Deferrability: 7" + ) + ) + } + + test("The result of retrieving type information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet = metaData.getTypeInfo() + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val typeName = resultSet.getString("TYPE_NAME") + val dataType = resultSet.getInt("DATA_TYPE") + val precision = resultSet.getInt("PRECISION") + val literalPrefix = resultSet.getString("LITERAL_PREFIX") + val literalSuffix = resultSet.getString("LITERAL_SUFFIX") + val createParams = resultSet.getString("CREATE_PARAMS") + val nullable = resultSet.getShort("NULLABLE") + val caseSensitive = resultSet.getBoolean("CASE_SENSITIVE") + val searchable = resultSet.getShort("SEARCHABLE") + val unsignedAttribute = resultSet.getBoolean("UNSIGNED_ATTRIBUTE") + val fixedPrecScale = resultSet.getBoolean("FIXED_PREC_SCALE") + val autoIncrement = resultSet.getBoolean("AUTO_INCREMENT") + val localTypeName = resultSet.getString("LOCAL_TYPE_NAME") + val minimumScale = resultSet.getShort("MINIMUM_SCALE") + val maximumScale = resultSet.getShort("MAXIMUM_SCALE") + val sqlDataType = resultSet.getShort("SQL_DATA_TYPE") + val sqlDatetimeSub = resultSet.getShort("SQL_DATETIME_SUB") + val numPrecRadix = resultSet.getShort("NUM_PREC_RADIX") + builder += s"Type Name: $typeName, Data Type: $dataType, Precision: $precision, Literal Prefix: $literalPrefix, Literal Suffix: $literalSuffix, Create Params: $createParams, Nullable: $nullable, Case Sensitive: $caseSensitive, Searchable: $searchable, Unsigned Attribute: $unsignedAttribute, Fixed Prec Scale: $fixedPrecScale, Auto Increment: $autoIncrement, Local Type Name: $localTypeName, Minimum Scale: $minimumScale, Maximum Scale: $maximumScale, SQL Data Type: $sqlDataType, SQL Datetime Sub: $sqlDatetimeSub, Num Prec Radix: $numPrecRadix" + builder.result() + }, + Vector( + "Type Name: BIT, Data Type: -7, Precision: 1, Literal Prefix: , Literal Suffix: , Create Params: [(M)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: BIT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: TINYINT, Data Type: -6, Precision: 3, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: TINYINT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: TINYINT UNSIGNED, Data Type: -6, Precision: 3, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: TINYINT UNSIGNED, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: BIGINT, Data Type: -5, Precision: 19, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: BIGINT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: BIGINT UNSIGNED, Data Type: -5, Precision: 20, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: BIGINT UNSIGNED, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: LONG VARBINARY, Data Type: -4, Precision: 16777215, Literal Prefix: ', Literal Suffix: ', Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: MEDIUMBLOB, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: MEDIUMBLOB, Data Type: -4, Precision: 16777215, Literal Prefix: ', Literal Suffix: ', Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: MEDIUMBLOB, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: LONGBLOB, Data Type: -4, Precision: 2147483647, Literal Prefix: ', Literal Suffix: ', Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: LONGBLOB, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: BLOB, Data Type: -4, Precision: 65535, Literal Prefix: ', Literal Suffix: ', Create Params: [(M)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: BLOB, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: VECTOR, Data Type: -4, Precision: 65532, Literal Prefix: ', Literal Suffix: ', Create Params: [(M)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: VECTOR, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: VARBINARY, Data Type: -3, Precision: 65535, Literal Prefix: ', Literal Suffix: ', Create Params: (M), Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: VARBINARY, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: TINYBLOB, Data Type: -3, Precision: 255, Literal Prefix: ', Literal Suffix: ', Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: TINYBLOB, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: BINARY, Data Type: -2, Precision: 255, Literal Prefix: ', Literal Suffix: ', Create Params: (M), Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: BINARY, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: LONG VARCHAR, Data Type: -1, Precision: 16777215, Literal Prefix: ', Literal Suffix: ', Create Params: [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: MEDIUMTEXT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: MEDIUMTEXT, Data Type: -1, Precision: 16777215, Literal Prefix: ', Literal Suffix: ', Create Params: [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: MEDIUMTEXT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: LONGTEXT, Data Type: -1, Precision: 2147483647, Literal Prefix: ', Literal Suffix: ', Create Params: [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: LONGTEXT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: TEXT, Data Type: -1, Precision: 65535, Literal Prefix: ', Literal Suffix: ', Create Params: [(M)] [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: TEXT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: CHAR, Data Type: 1, Precision: 255, Literal Prefix: ', Literal Suffix: ', Create Params: [(M)] [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: CHAR, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: ENUM, Data Type: 1, Precision: 65535, Literal Prefix: ', Literal Suffix: ', Create Params: ('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: ENUM, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: SET, Data Type: 1, Precision: 64, Literal Prefix: ', Literal Suffix: ', Create Params: ('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: SET, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DECIMAL, Data Type: 3, Precision: 65, Literal Prefix: , Literal Suffix: , Create Params: [(M[,D])] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DECIMAL, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: NUMERIC, Data Type: 3, Precision: 65, Literal Prefix: , Literal Suffix: , Create Params: [(M[,D])] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DECIMAL, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: INTEGER, Data Type: 4, Precision: 10, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: INT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: INT, Data Type: 4, Precision: 10, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: INT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: MEDIUMINT, Data Type: 4, Precision: 7, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: MEDIUMINT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: INTEGER UNSIGNED, Data Type: 4, Precision: 10, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: INT UNSIGNED, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: INT UNSIGNED, Data Type: 4, Precision: 10, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: INT UNSIGNED, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: MEDIUMINT UNSIGNED, Data Type: 4, Precision: 8, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: MEDIUMINT UNSIGNED, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: SMALLINT, Data Type: 5, Precision: 5, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: SMALLINT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: SMALLINT UNSIGNED, Data Type: 5, Precision: 5, Literal Prefix: , Literal Suffix: , Create Params: [(M)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: SMALLINT UNSIGNED, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: FLOAT, Data Type: 7, Precision: 12, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: FLOAT, Minimum Scale: -38, Maximum Scale: 38, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DOUBLE, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DOUBLE PRECISION, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: REAL, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DOUBLE UNSIGNED, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE UNSIGNED, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DOUBLE PRECISION UNSIGNED, Data Type: 8, Precision: 22, Literal Prefix: , Literal Suffix: , Create Params: [(M,D)] [UNSIGNED] [ZEROFILL], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: true, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DOUBLE UNSIGNED, Minimum Scale: -308, Maximum Scale: 308, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: VARCHAR, Data Type: 12, Precision: 65535, Literal Prefix: ', Literal Suffix: ', Create Params: (M) [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: VARCHAR, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: TINYTEXT, Data Type: 12, Precision: 255, Literal Prefix: ', Literal Suffix: ', Create Params: [CHARACTER SET charset_name] [COLLATE collation_name], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: TINYTEXT, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: BOOL, Data Type: 16, Precision: 3, Literal Prefix: , Literal Suffix: , Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: true, Local Type Name: BOOLEAN, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DATE, Data Type: 91, Precision: 10, Literal Prefix: ', Literal Suffix: ', Create Params: , Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DATE, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: YEAR, Data Type: 91, Precision: 4, Literal Prefix: , Literal Suffix: , Create Params: [(4)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: YEAR, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: TIME, Data Type: 92, Precision: 16, Literal Prefix: ', Literal Suffix: ', Create Params: [(fsp)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: TIME, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: DATETIME, Data Type: 93, Precision: 26, Literal Prefix: ', Literal Suffix: ', Create Params: [(fsp)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: DATETIME, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10", + "Type Name: TIMESTAMP, Data Type: 93, Precision: 26, Literal Prefix: ', Literal Suffix: ', Create Params: [(fsp)], Nullable: 1, Case Sensitive: true, Searchable: 3, Unsigned Attribute: false, Fixed Prec Scale: false, Auto Increment: false, Local Type Name: TIMESTAMP, Minimum Scale: 0, Maximum Scale: 0, SQL Data Type: 0, SQL Datetime Sub: 0, Num Prec Radix: 10" + ) + ) + } + + test("The result of retrieving index information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- + metaData.getIndexInfo(Some("world"), None, Some("city"), true, true) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val nonUnique = resultSet.getBoolean("NON_UNIQUE") + val indexQualifier = resultSet.getString("INDEX_QUALIFIER") + val INDEXNAME = resultSet.getString("INDEX_NAME") + val `type` = resultSet.getShort("TYPE") + val ordinalPosition = resultSet.getShort("ORDINAL_POSITION") + val columnName = resultSet.getString("COLUMN_NAME") + val ascOrDesc = resultSet.getString("ASC_OR_DESC") + val pages = resultSet.getLong("PAGES") + val filterCondition = resultSet.getString("FILTER_CONDITION") + builder += s"Table Cat: $tableCat, Table Schem: $tableSchem, Table Name: $tableName, Non Unique: $nonUnique, Index Qualifier: $indexQualifier, Index Name: $INDEXNAME, Type: ${ `type` }, Ordinal Position: $ordinalPosition, Column Name: $columnName, Asc Or Desc: $ascOrDesc, Pages: $pages, Filter Condition: $filterCondition" + builder.result() + }, + Vector( + "Table Cat: world, Table Schem: null, Table Name: city, Non Unique: false, Index Qualifier: null, Index Name: PRIMARY, Type: 3, Ordinal Position: 1, Column Name: ID, Asc Or Desc: A, Pages: 0, Filter Condition: null" + ) + ) + } + + test("The result of retrieving function information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getFunctions(Some("sys"), None, None) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val functionCat = resultSet.getString("FUNCTION_CAT") + val functionSchem = resultSet.getString("FUNCTION_SCHEM") + val functionName = resultSet.getString("FUNCTION_NAME") + val functionType = resultSet.getShort("FUNCTION_TYPE") + val specificName = resultSet.getString("SPECIFIC_NAME") + builder += s"Function Cat: $functionCat, Function Schem: $functionSchem, Function Name: $functionName, Function Type: $functionType, Specific Name: $specificName" + builder.result() + }, + Vector( + "Function Cat: sys, Function Schem: null, Function Name: extract_schema_from_file_name, Function Type: 1, Specific Name: extract_schema_from_file_name", + "Function Cat: sys, Function Schem: null, Function Name: extract_table_from_file_name, Function Type: 1, Specific Name: extract_table_from_file_name", + "Function Cat: sys, Function Schem: null, Function Name: format_bytes, Function Type: 1, Specific Name: format_bytes", + "Function Cat: sys, Function Schem: null, Function Name: format_path, Function Type: 1, Specific Name: format_path", + "Function Cat: sys, Function Schem: null, Function Name: format_statement, Function Type: 1, Specific Name: format_statement", + "Function Cat: sys, Function Schem: null, Function Name: format_time, Function Type: 1, Specific Name: format_time", + "Function Cat: sys, Function Schem: null, Function Name: list_add, Function Type: 1, Specific Name: list_add", + "Function Cat: sys, Function Schem: null, Function Name: list_drop, Function Type: 1, Specific Name: list_drop", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_account_enabled, Function Type: 1, Specific Name: ps_is_account_enabled", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_consumer_enabled, Function Type: 1, Specific Name: ps_is_consumer_enabled", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_instrument_default_enabled, Function Type: 1, Specific Name: ps_is_instrument_default_enabled", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_instrument_default_timed, Function Type: 1, Specific Name: ps_is_instrument_default_timed", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_thread_instrumented, Function Type: 1, Specific Name: ps_is_thread_instrumented", + "Function Cat: sys, Function Schem: null, Function Name: ps_thread_account, Function Type: 1, Specific Name: ps_thread_account", + "Function Cat: sys, Function Schem: null, Function Name: ps_thread_id, Function Type: 1, Specific Name: ps_thread_id", + "Function Cat: sys, Function Schem: null, Function Name: ps_thread_stack, Function Type: 1, Specific Name: ps_thread_stack", + "Function Cat: sys, Function Schem: null, Function Name: ps_thread_trx_info, Function Type: 1, Specific Name: ps_thread_trx_info", + "Function Cat: sys, Function Schem: null, Function Name: quote_identifier, Function Type: 1, Specific Name: quote_identifier", + "Function Cat: sys, Function Schem: null, Function Name: sys_get_config, Function Type: 1, Specific Name: sys_get_config", + "Function Cat: sys, Function Schem: null, Function Name: version_major, Function Type: 1, Specific Name: version_major", + "Function Cat: sys, Function Schem: null, Function Name: version_minor, Function Type: 1, Specific Name: version_minor", + "Function Cat: sys, Function Schem: null, Function Name: version_patch, Function Type: 1, Specific Name: version_patch" + ) + ) + } + + test("The result of retrieving function column information matches the specified value.") { + assertIO( + connection().use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getFunctionColumns(Some("sys"), None, None, Some("in_host")) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val functionCat = resultSet.getString("FUNCTION_CAT") + val functionSchem = resultSet.getString("FUNCTION_SCHEM") + val functionName = resultSet.getString("FUNCTION_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val columnType = resultSet.getShort("COLUMN_TYPE") + val dataType = resultSet.getInt("DATA_TYPE") + val typeName = resultSet.getString("TYPE_NAME") + val precision = resultSet.getInt("PRECISION") + val length = resultSet.getInt("LENGTH") + val scale = resultSet.getShort("SCALE") + val radix = resultSet.getShort("RADIX") + val nullable = resultSet.getShort("NULLABLE") + val remarks = resultSet.getString("REMARKS") + val charOctetLength = resultSet.getInt("CHAR_OCTET_LENGTH") + val ordinalPosition = resultSet.getInt("ORDINAL_POSITION") + val isNullable = resultSet.getString("IS_NULLABLE") + val specificName = resultSet.getString("SPECIFIC_NAME") + builder += s"Function Cat: $functionCat, Function Schem: $functionSchem, Function Name: $functionName, Column Name: $columnName, Column Type: $columnType, Data Type: $dataType, Type Name: $typeName, Precision: $precision, Length: $length, Scale: $scale, Radix: $radix, Nullable: $nullable, Remarks: $remarks, Char Octet Length: $charOctetLength, Ordinal Position: $ordinalPosition, Is Nullable: $isNullable, Specific Name: $specificName" + + builder.result() + }, + Vector( + "Function Cat: sys, Function Schem: null, Function Name: extract_schema_from_file_name, Column Name: , Column Type: 4, Data Type: 12, Type Name: VARCHAR, Precision: 0, Length: 64, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 256, Ordinal Position: 0, Is Nullable: YES, Specific Name: extract_schema_from_file_name", + "Function Cat: sys, Function Schem: null, Function Name: extract_table_from_file_name, Column Name: , Column Type: 4, Data Type: 12, Type Name: VARCHAR, Precision: 0, Length: 64, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 256, Ordinal Position: 0, Is Nullable: YES, Specific Name: extract_table_from_file_name", + "Function Cat: sys, Function Schem: null, Function Name: format_bytes, Column Name: , Column Type: 4, Data Type: -1, Type Name: TEXT, Precision: 0, Length: 65535, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 65535, Ordinal Position: 0, Is Nullable: YES, Specific Name: format_bytes", + "Function Cat: sys, Function Schem: null, Function Name: format_path, Column Name: , Column Type: 4, Data Type: 12, Type Name: VARCHAR, Precision: 0, Length: 512, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 2048, Ordinal Position: 0, Is Nullable: YES, Specific Name: format_path", + "Function Cat: sys, Function Schem: null, Function Name: format_statement, Column Name: , Column Type: 4, Data Type: -1, Type Name: LONGTEXT, Precision: 0, Length: 2147483647, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 2147483647, Ordinal Position: 0, Is Nullable: YES, Specific Name: format_statement", + "Function Cat: sys, Function Schem: null, Function Name: format_time, Column Name: , Column Type: 4, Data Type: -1, Type Name: TEXT, Precision: 0, Length: 65535, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 65535, Ordinal Position: 0, Is Nullable: YES, Specific Name: format_time", + "Function Cat: sys, Function Schem: null, Function Name: list_add, Column Name: , Column Type: 4, Data Type: -1, Type Name: TEXT, Precision: 0, Length: 65535, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 65535, Ordinal Position: 0, Is Nullable: YES, Specific Name: list_add", + "Function Cat: sys, Function Schem: null, Function Name: list_drop, Column Name: , Column Type: 4, Data Type: -1, Type Name: TEXT, Precision: 0, Length: 65535, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 65535, Ordinal Position: 0, Is Nullable: YES, Specific Name: list_drop", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_account_enabled, Column Name: , Column Type: 4, Data Type: 1, Type Name: ENUM, Precision: 0, Length: 3, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 12, Ordinal Position: 0, Is Nullable: YES, Specific Name: ps_is_account_enabled", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_account_enabled, Column Name: in_host, Column Type: 1, Data Type: 12, Type Name: VARCHAR, Precision: 0, Length: 255, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 1020, Ordinal Position: 1, Is Nullable: YES, Specific Name: ps_is_account_enabled", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_consumer_enabled, Column Name: , Column Type: 4, Data Type: 1, Type Name: ENUM, Precision: 0, Length: 3, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 12, Ordinal Position: 0, Is Nullable: YES, Specific Name: ps_is_consumer_enabled", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_instrument_default_enabled, Column Name: , Column Type: 4, Data Type: 1, Type Name: ENUM, Precision: 0, Length: 3, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 12, Ordinal Position: 0, Is Nullable: YES, Specific Name: ps_is_instrument_default_enabled", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_instrument_default_timed, Column Name: , Column Type: 4, Data Type: 1, Type Name: ENUM, Precision: 0, Length: 3, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 12, Ordinal Position: 0, Is Nullable: YES, Specific Name: ps_is_instrument_default_timed", + "Function Cat: sys, Function Schem: null, Function Name: ps_is_thread_instrumented, Column Name: , Column Type: 4, Data Type: 1, Type Name: ENUM, Precision: 0, Length: 7, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 28, Ordinal Position: 0, Is Nullable: YES, Specific Name: ps_is_thread_instrumented", + "Function Cat: sys, Function Schem: null, Function Name: ps_thread_account, Column Name: , Column Type: 4, Data Type: -1, Type Name: TEXT, Precision: 0, Length: 65535, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 65535, Ordinal Position: 0, Is Nullable: YES, Specific Name: ps_thread_account", + "Function Cat: sys, Function Schem: null, Function Name: ps_thread_id, Column Name: , Column Type: 4, Data Type: -5, Type Name: BIGINT UNSIGNED, Precision: 20, Length: 20, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 0, Ordinal Position: 0, Is Nullable: YES, Specific Name: ps_thread_id", + "Function Cat: sys, Function Schem: null, Function Name: ps_thread_stack, Column Name: , Column Type: 4, Data Type: -1, Type Name: LONGTEXT, Precision: 0, Length: 2147483647, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 2147483647, Ordinal Position: 0, Is Nullable: YES, Specific Name: ps_thread_stack", + "Function Cat: sys, Function Schem: null, Function Name: ps_thread_trx_info, Column Name: , Column Type: 4, Data Type: -1, Type Name: LONGTEXT, Precision: 0, Length: 2147483647, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 2147483647, Ordinal Position: 0, Is Nullable: YES, Specific Name: ps_thread_trx_info", + "Function Cat: sys, Function Schem: null, Function Name: quote_identifier, Column Name: , Column Type: 4, Data Type: -1, Type Name: TEXT, Precision: 0, Length: 65535, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 65535, Ordinal Position: 0, Is Nullable: YES, Specific Name: quote_identifier", + "Function Cat: sys, Function Schem: null, Function Name: sys_get_config, Column Name: , Column Type: 4, Data Type: 12, Type Name: VARCHAR, Precision: 0, Length: 128, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 512, Ordinal Position: 0, Is Nullable: YES, Specific Name: sys_get_config", + "Function Cat: sys, Function Schem: null, Function Name: version_major, Column Name: , Column Type: 4, Data Type: -6, Type Name: TINYINT UNSIGNED, Precision: 3, Length: 3, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 0, Ordinal Position: 0, Is Nullable: YES, Specific Name: version_major", + "Function Cat: sys, Function Schem: null, Function Name: version_minor, Column Name: , Column Type: 4, Data Type: -6, Type Name: TINYINT UNSIGNED, Precision: 3, Length: 3, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 0, Ordinal Position: 0, Is Nullable: YES, Specific Name: version_minor", + "Function Cat: sys, Function Schem: null, Function Name: version_patch, Column Name: , Column Type: 4, Data Type: -6, Type Name: TINYINT UNSIGNED, Precision: 3, Length: 3, Scale: 0, Radix: 10, Nullable: 1, Remarks: null, Char Octet Length: 0, Ordinal Position: 0, Is Nullable: YES, Specific Name: version_patch" + ) + ) + } + + test("The result of retrieving columns information matches the specified value.") { + assertIO( + connection("SCHEMA").use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getColumns(None, None, Some("privileges_table"), None) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val dataType = resultSet.getInt("DATA_TYPE") + val typeName = resultSet.getString("TYPE_NAME") + val columnSize = resultSet.getInt("COLUMN_SIZE") + val bufferLength = resultSet.getInt("BUFFER_LENGTH") + val decimalDigits = resultSet.getInt("DECIMAL_DIGITS") + val numPrecRadix = resultSet.getInt("NUM_PREC_RADIX") + val nullable = resultSet.getInt("NULLABLE") + val remarks = resultSet.getString("REMARKS") + val columnDef = resultSet.getString("COLUMN_DEF") + val sqlDataType = resultSet.getInt("SQL_DATA_TYPE") + val sqlDatetimeSub = resultSet.getInt("SQL_DATETIME_SUB") + val charOctetLength = resultSet.getInt("CHAR_OCTET_LENGTH") + val ordinalPosition = resultSet.getInt("ORDINAL_POSITION") + val isNullable = resultSet.getString("IS_NULLABLE") + val scopeCatalog = resultSet.getString("SCOPE_CATALOG") + val scopeSchema = resultSet.getString("SCOPE_SCHEMA") + val scopeTable = resultSet.getString("SCOPE_TABLE") + val sourceDataType = resultSet.getShort("SOURCE_DATA_TYPE") + val isAutoincrement = resultSet.getString("IS_AUTOINCREMENT") + val isGeneratedcolumn = resultSet.getString("IS_GENERATEDCOLUMN") + builder += s"Table Cat: $tableCat, Table Schem: $tableSchem, Table Name: $tableName, Column Name: $columnName, Data Type: $dataType, Type Name: $typeName, Column Size: $columnSize, Buffer Length: $bufferLength, Decimal Digits: $decimalDigits, Num Prec Radix: $numPrecRadix, Nullable: $nullable, Remarks: $remarks, Column Def: $columnDef, SQL Data Type: $sqlDataType, SQL Datetime Sub: $sqlDatetimeSub, Char Octet Length: $charOctetLength, Ordinal Position: $ordinalPosition, Is Nullable: $isNullable, Scope Catalog: $scopeCatalog, Scope Schema: $scopeSchema, Scope Table: $scopeTable, Source Data Type: $sourceDataType, Is Autoincrement: $isAutoincrement, Is Generatedcolumn: $isGeneratedcolumn" + builder.result() + }, + Vector( + "Table Cat: def, Table Schem: connector_test, Table Name: privileges_table, Column Name: c1, Data Type: 4, Type Name: INT, Column Size: 10, Buffer Length: 65535, Decimal Digits: 0, Num Prec Radix: 10, Nullable: 0, Remarks: , Column Def: null, SQL Data Type: 0, SQL Datetime Sub: 0, Char Octet Length: 0, Ordinal Position: 1, Is Nullable: NO, Scope Catalog: null, Scope Schema: null, Scope Table: null, Source Data Type: 0, Is Autoincrement: NO, Is Generatedcolumn: NO", + "Table Cat: def, Table Schem: connector_test, Table Name: privileges_table, Column Name: c2, Data Type: 4, Type Name: INT, Column Size: 10, Buffer Length: 65535, Decimal Digits: 0, Num Prec Radix: 10, Nullable: 0, Remarks: , Column Def: null, SQL Data Type: 0, SQL Datetime Sub: 0, Char Octet Length: 0, Ordinal Position: 2, Is Nullable: NO, Scope Catalog: null, Scope Schema: null, Scope Table: null, Source Data Type: 0, Is Autoincrement: NO, Is Generatedcolumn: NO", + "Table Cat: def, Table Schem: connector_test, Table Name: privileges_table, Column Name: updated_at, Data Type: 93, Type Name: TIMESTAMP, Column Size: 19, Buffer Length: 65535, Decimal Digits: 0, Num Prec Radix: 10, Nullable: 0, Remarks: , Column Def: CURRENT_TIMESTAMP, SQL Data Type: 0, SQL Datetime Sub: 0, Char Octet Length: 0, Ordinal Position: 3, Is Nullable: NO, Scope Catalog: null, Scope Schema: null, Scope Table: null, Source Data Type: 0, Is Autoincrement: NO, Is Generatedcolumn: YES" + ) + ) + } diff --git a/tests/src/test/scala/ldbc/tests/DatabaseMetaDataTest.scala b/tests/src/test/scala/ldbc/tests/DatabaseMetaDataTest.scala new file mode 100644 index 000000000..6ed6d805c --- /dev/null +++ b/tests/src/test/scala/ldbc/tests/DatabaseMetaDataTest.scala @@ -0,0 +1,2389 @@ +/** + * Copyright (c) 2023-2024 by Takahiko Tominaga + * This software is licensed under the MIT License (MIT). + * For more information see LICENSE or https://opensource.org/licenses/MIT + */ + +package ldbc.tests + +import com.mysql.cj.jdbc.MysqlDataSource + +import cats.effect.* + +import org.typelevel.otel4s.trace.Tracer + +import munit.* + +import ldbc.sql.* +import ldbc.connector.SSL + +class LdbcDatabaseMetaDataTest extends DatabaseMetaDataTest: + override def prefix: "ldbc" = "ldbc" + + override def connection: Resource[IO, Connection[IO]] = + ldbc.connector.Connection[IO]( + host = host, + port = port, + user = user, + password = Some(password), + database = Some(database), + ssl = SSL.Trusted + ) + +class JdbcDatabaseMetaDataTest extends DatabaseMetaDataTest: + + val ds = new MysqlDataSource() + ds.setServerName(host) + ds.setPortNumber(port) + ds.setDatabaseName(database) + ds.setUser(user) + ds.setPassword(password) + + override def prefix: "jdbc" | "ldbc" = "jdbc" + + override def connection: Resource[IO, Connection[IO]] = + Resource.make(jdbc.connector.MysqlDataSource[IO](ds).getConnection)(_.close()) + +trait DatabaseMetaDataTest extends CatsEffectSuite: + + given Tracer[IO] = Tracer.noop[IO] + + protected val host: String = "127.0.0.1" + protected val port: Int = 13306 + protected val user: String = "ldbc" + protected val password: String = "password" + protected val database: String = "connector_test" + + def prefix: "jdbc" | "ldbc" + def connection: Resource[IO, Connection[IO]] + + test(s"$prefix: allTablesAreSelectable") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.allTablesAreSelectable() + }, + false + ) + } + + test(s"$prefix: getURL") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getURL() + }, + s"jdbc:mysql://$host:$port/$database" + ) + } + + test(s"$prefix: getUserName") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + value <- metaData.getUserName() + yield value + }, + s"$user@172.18.0.1" + ) + } + + test(s"$prefix: isReadOnly") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.isReadOnly() + }, + false + ) + } + + test(s"$prefix: nullsAreSortedHigh") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.nullsAreSortedHigh() + }, + false + ) + } + + test(s"$prefix: nullsAreSortedLow") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.nullsAreSortedLow() + }, + true + ) + } + + test(s"$prefix: nullsAreSortedAtStart") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.nullsAreSortedAtStart() + }, + false + ) + } + + test(s"$prefix: nullsAreSortedAtEnd") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.nullsAreSortedAtEnd() + }, + false + ) + } + + test(s"$prefix: getDatabaseProductName") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getDatabaseProductName() + }, + "MySQL" + ) + } + + test(s"$prefix: getDatabaseProductVersion") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getDatabaseProductVersion() + }, + "8.4.0" + ) + } + + test(s"$prefix: getDriverName") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getDriverName() + }, + if prefix == "ldbc" then "MySQL Connector/L" else "MySQL Connector/J" + ) + } + + test(s"$prefix: getDriverVersion") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getDriverVersion() + }, + if prefix == "jdbc" then "mysql-connector-j-8.4.0 (Revision: 1c3f5c149e0bfe31c7fbeb24e2d260cd890972c4)" + else "ldbc-connector-0.3.0" + ) + } + + test(s"$prefix: getDriverMajorVersion") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getDriverMajorVersion() + }, + if prefix == "jdbc" then 8 else 0 + ) + } + + test(s"$prefix: getDriverMinorVersion") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getDriverMinorVersion() + }, + if prefix == "jdbc" then 4 else 3 + ) + } + + test(s"$prefix: usesLocalFiles") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.usesLocalFiles() + }, + false + ) + } + + test(s"$prefix: usesLocalFilePerTable") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.usesLocalFilePerTable() + }, + false + ) + } + + test(s"$prefix: supportsMixedCaseIdentifiers") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsMixedCaseIdentifiers() + }, + true + ) + } + + test(s"$prefix: storesUpperCaseIdentifiers") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.storesUpperCaseIdentifiers() + }, + false + ) + } + + test(s"$prefix: storesLowerCaseIdentifiers") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.storesLowerCaseIdentifiers() + }, + false + ) + } + + test(s"$prefix: storesMixedCaseIdentifiers") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.storesMixedCaseIdentifiers() + }, + true + ) + } + + test(s"$prefix: supportsMixedCaseQuotedIdentifiers") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsMixedCaseQuotedIdentifiers() + }, + true + ) + } + + test(s"$prefix: storesUpperCaseQuotedIdentifiers") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.storesUpperCaseQuotedIdentifiers() + }, + true + ) + } + + test(s"$prefix: storesLowerCaseQuotedIdentifiers") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.storesLowerCaseQuotedIdentifiers() + }, + false + ) + } + + test(s"$prefix: storesMixedCaseQuotedIdentifiers") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.storesMixedCaseQuotedIdentifiers() + }, + true + ) + } + + test(s"$prefix: getIdentifierQuoteString") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getIdentifierQuoteString() + }, + "`" + ) + } + + test(s"$prefix: getSQLKeywords") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + value <- metaData.getSQLKeywords() + yield value + }, + "ACCESSIBLE,ADD,ANALYZE,ASC,BEFORE,CASCADE,CHANGE,CONTINUE,DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,DESC,DISTINCTROW,DIV,DUAL,ELSEIF,EMPTY,ENCLOSED,ESCAPED,EXIT,EXPLAIN,FIRST_VALUE,FLOAT4,FLOAT8,FORCE,FULLTEXT,GENERATED,GROUPS,HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,INDEX,INFILE,INT1,INT2,INT3,INT4,INT8,IO_AFTER_GTIDS,IO_BEFORE_GTIDS,ITERATE,JSON_TABLE,KEY,KEYS,KILL,LAG,LAST_VALUE,LEAD,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY,MAXVALUE,MEDIUMBLOB,MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND,NO_WRITE_TO_BINLOG,NTH_VALUE,NTILE,OPTIMIZE,OPTIMIZER_COSTS,OPTION,OPTIONALLY,OUTFILE,PURGE,READ,READ_WRITE,REGEXP,RENAME,REPEAT,REPLACE,REQUIRE,RESIGNAL,RESTRICT,RLIKE,SCHEMA,SCHEMAS,SECOND_MICROSECOND,SEPARATOR,SHOW,SIGNAL,SPATIAL,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT,SSL,STARTING,STORED,STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT,TINYTEXT,UNDO,UNLOCK,UNSIGNED,USAGE,USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,VARCHARACTER,VIRTUAL,WHILE,WRITE,XOR,YEAR_MONTH,ZEROFILL" + ) + } + + test(s"$prefix: getNumericFunctions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getNumericFunctions() + }, + "ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,COT,DEGREES,EXP,FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW,POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE" + ) + } + + test(s"$prefix: getStringFunctions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getStringFunctions() + }, + "ASCII,BIN,BIT_LENGTH,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,CONCAT_WS,CONV,ELT,EXPORT_SET,FIELD,FIND_IN_SET,HEX,INSERT,INSTR,LCASE,LEFT,LENGTH,LOAD_FILE,LOCATE,LOCATE,LOWER,LPAD,LTRIM,MAKE_SET,MATCH,MID,OCT,OCTET_LENGTH,ORD,POSITION,QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,SPACE,STRCMP,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING_INDEX,TRIM,UCASE,UPPER" + ) + } + + test(s"$prefix: getSystemFunctions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getSystemFunctions() + }, + "DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION" + ) + } + + test(s"$prefix: getTimeDateFunctions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getTimeDateFunctions() + }, + "DAYOFWEEK,WEEKDAY,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME,MONTHNAME,QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,PERIOD_ADD,PERIOD_DIFF,TO_DAYS,FROM_DAYS,DATE_FORMAT,TIME_FORMAT,CURDATE,CURRENT_DATE,CURTIME,CURRENT_TIME,NOW,SYSDATE,CURRENT_TIMESTAMP,UNIX_TIMESTAMP,FROM_UNIXTIME,SEC_TO_TIME,TIME_TO_SEC" + ) + } + + test(s"$prefix: getSearchStringEscape") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getSearchStringEscape() + }, + "\\" + ) + } + + test(s"$prefix: getExtraNameCharacters") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getExtraNameCharacters() + }, + "$" + ) + } + + test(s"$prefix: supportsAlterTableWithAddColumn") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsAlterTableWithAddColumn() + }, + true + ) + } + + test(s"$prefix: supportsAlterTableWithDropColumn") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsAlterTableWithDropColumn() + }, + true + ) + } + + test(s"$prefix: supportsColumnAliasing") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsColumnAliasing() + }, + true + ) + } + + test(s"$prefix: nullPlusNonNullIsNull") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.nullPlusNonNullIsNull() + }, + true + ) + } + + test(s"$prefix: supportsConvert") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsConvert() + }, + false + ) + } + + test(s"$prefix: supportsTableCorrelationNames") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsTableCorrelationNames() + }, + true + ) + } + + test(s"$prefix: supportsDifferentTableCorrelationNames") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsDifferentTableCorrelationNames() + }, + true + ) + } + + test(s"$prefix: supportsExpressionsInOrderBy") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsExpressionsInOrderBy() + }, + true + ) + } + + test(s"$prefix: supportsOrderByUnrelated") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsOrderByUnrelated() + }, + false + ) + } + + test(s"$prefix: supportsGroupBy") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsGroupBy() + }, + true + ) + } + + test(s"$prefix: supportsGroupByUnrelated") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsGroupByUnrelated() + }, + true + ) + } + + test(s"$prefix: supportsGroupByBeyondSelect") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsGroupByBeyondSelect() + }, + true + ) + } + + test(s"$prefix: supportsLikeEscapeClause") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsLikeEscapeClause() + }, + true + ) + } + + test(s"$prefix: supportsMultipleResultSets") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsMultipleResultSets() + }, + true + ) + } + + test(s"$prefix: supportsMultipleTransactions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsMultipleTransactions() + }, + true + ) + } + + test(s"$prefix: supportsNonNullableColumns") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsNonNullableColumns() + }, + true + ) + } + + test(s"$prefix: supportsMinimumSQLGrammar") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsMinimumSQLGrammar() + }, + true + ) + } + + test(s"$prefix: supportsCoreSQLGrammar") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsCoreSQLGrammar() + }, + true + ) + } + + test(s"$prefix: supportsExtendedSQLGrammar") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsExtendedSQLGrammar() + }, + false + ) + } + + test(s"$prefix: supportsANSI92EntryLevelSQL") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsANSI92EntryLevelSQL() + }, + true + ) + } + + test(s"$prefix: supportsANSI92IntermediateSQL") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsANSI92IntermediateSQL() + }, + false + ) + } + + test(s"$prefix: supportsANSI92FullSQL") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsANSI92FullSQL() + }, + false + ) + } + + test(s"$prefix: supportsIntegrityEnhancementFacility") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsIntegrityEnhancementFacility() + }, + false + ) + } + + test(s"$prefix: supportsOuterJoins") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsOuterJoins() + }, + true + ) + } + + test(s"$prefix: supportsLimitedOuterJoins") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsLimitedOuterJoins() + }, + true + ) + } + + test(s"$prefix: getSchemaTerm") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getSchemaTerm() + }, + "" + ) + } + + test(s"$prefix: getProcedureTerm") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getProcedureTerm() + }, + "PROCEDURE" + ) + } + + test(s"$prefix: getCatalogTerm") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getCatalogTerm() + }, + "database" + ) + } + + test(s"$prefix: isCatalogAtStart") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.isCatalogAtStart() + }, + true + ) + } + + test(s"$prefix: getCatalogSeparator") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getCatalogSeparator() + }, + "." + ) + } + + test(s"$prefix: supportsSchemasInDataManipulation") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSchemasInDataManipulation() + }, + false + ) + } + + test(s"$prefix: supportsSchemasInProcedureCalls") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSchemasInProcedureCalls() + }, + false + ) + } + + test(s"$prefix: supportsSchemasInTableDefinitions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSchemasInTableDefinitions() + }, + false + ) + } + + test(s"$prefix: supportsSchemasInIndexDefinitions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSchemasInIndexDefinitions() + }, + false + ) + } + + test(s"$prefix: supportsSchemasInPrivilegeDefinitions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSchemasInPrivilegeDefinitions() + }, + false + ) + } + + test(s"$prefix: supportsCatalogsInDataManipulation") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsCatalogsInDataManipulation() + }, + true + ) + } + + test(s"$prefix: supportsCatalogsInProcedureCalls") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsCatalogsInProcedureCalls() + }, + true + ) + } + + test(s"$prefix: supportsCatalogsInTableDefinitions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsCatalogsInTableDefinitions() + }, + true + ) + } + + test(s"$prefix: supportsCatalogsInIndexDefinitions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsCatalogsInIndexDefinitions() + }, + true + ) + } + + test(s"$prefix: supportsCatalogsInPrivilegeDefinitions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsCatalogsInPrivilegeDefinitions() + }, + true + ) + } + + test(s"$prefix: supportsPositionedDelete") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsPositionedDelete() + }, + false + ) + } + + test(s"$prefix: supportsPositionedUpdate") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsPositionedUpdate() + }, + false + ) + } + + test(s"$prefix: supportsSelectForUpdate") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSelectForUpdate() + }, + true + ) + } + + test(s"$prefix: supportsStoredProcedures") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsStoredProcedures() + }, + true + ) + } + + test(s"$prefix: supportsSubqueriesInComparisons") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSubqueriesInComparisons() + }, + true + ) + } + + test(s"$prefix: supportsSubqueriesInExists") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSubqueriesInExists() + }, + true + ) + } + + test(s"$prefix: supportsSubqueriesInIns") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSubqueriesInIns() + }, + true + ) + } + + test(s"$prefix: supportsSubqueriesInQuantifieds") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSubqueriesInQuantifieds() + }, + true + ) + } + + test(s"$prefix: supportsCorrelatedSubqueries") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsCorrelatedSubqueries() + }, + true + ) + } + + test(s"$prefix: supportsUnion") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsUnion() + }, + true + ) + } + + test(s"$prefix: supportsUnionAll") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsUnionAll() + }, + true + ) + } + + test(s"$prefix: supportsOpenCursorsAcrossCommit") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsOpenCursorsAcrossCommit() + }, + false + ) + } + + test(s"$prefix: supportsOpenCursorsAcrossRollback") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsOpenCursorsAcrossRollback() + }, + false + ) + } + + test(s"$prefix: supportsOpenStatementsAcrossCommit") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsOpenStatementsAcrossCommit() + }, + false + ) + } + + test(s"$prefix: supportsOpenStatementsAcrossRollback") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsOpenStatementsAcrossRollback() + }, + false + ) + } + + test(s"$prefix: getMaxBinaryLiteralLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxBinaryLiteralLength() + }, + 16777208 + ) + } + + test(s"$prefix: getMaxCharLiteralLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxCharLiteralLength() + }, + 16777208 + ) + } + + test(s"$prefix: getMaxColumnNameLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxColumnNameLength() + }, + 64 + ) + } + + test(s"$prefix: getMaxColumnsInGroupBy") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxColumnsInGroupBy() + }, + 64 + ) + } + + test(s"$prefix: getMaxColumnsInIndex") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxColumnsInIndex() + }, + 16 + ) + } + + test(s"$prefix: getMaxColumnsInOrderBy") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxColumnsInOrderBy() + }, + 64 + ) + } + + test(s"$prefix: getMaxColumnsInSelect") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxColumnsInSelect() + }, + 256 + ) + } + + test(s"$prefix: getMaxColumnsInTable") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxColumnsInTable() + }, + 512 + ) + } + + test(s"$prefix: getMaxConnections") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxConnections() + }, + 0 + ) + } + + test(s"$prefix: getMaxCursorNameLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxCursorNameLength() + }, + 64 + ) + } + + test(s"$prefix: getMaxIndexLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxIndexLength() + }, + 256 + ) + } + + test(s"$prefix: getMaxSchemaNameLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxSchemaNameLength() + }, + 0 + ) + } + + test(s"$prefix: getMaxProcedureNameLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxProcedureNameLength() + }, + 0 + ) + } + + test(s"$prefix: getMaxCatalogNameLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxCatalogNameLength() + }, + 32 + ) + } + + test(s"$prefix: getMaxRowSize") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxRowSize() + }, + 2147483639 + ) + } + + test(s"$prefix: doesMaxRowSizeIncludeBlobs") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.doesMaxRowSizeIncludeBlobs() + }, + true + ) + } + + test(s"$prefix: getMaxStatementLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxStatementLength() + }, + 65531 + ) + } + + test(s"$prefix: getMaxStatements") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxStatements() + }, + 0 + ) + } + + test(s"$prefix: getMaxTableNameLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxTableNameLength() + }, + 64 + ) + } + + test(s"$prefix: getMaxTablesInSelect") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxTablesInSelect() + }, + 256 + ) + } + + test(s"$prefix: getMaxUserNameLength") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxUserNameLength() + }, + 16 + ) + } + + test(s"$prefix: getDefaultTransactionIsolation") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getDefaultTransactionIsolation() + }, + 4 + ) + } + + test(s"$prefix: supportsTransactions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsTransactions() + }, + true + ) + } + + test(s"$prefix: supportsTransactionIsolationLevel") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsTransactionIsolationLevel(2) + }, + true + ) + } + + test(s"$prefix: supportsDataDefinitionAndDataManipulationTransactions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsDataDefinitionAndDataManipulationTransactions() + }, + false + ) + } + + test(s"$prefix: supportsDataManipulationTransactionsOnly") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsDataManipulationTransactionsOnly() + }, + false + ) + } + + test(s"$prefix: dataDefinitionCausesTransactionCommit") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.dataDefinitionCausesTransactionCommit() + }, + true + ) + } + + test(s"$prefix: dataDefinitionIgnoredInTransactions") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.dataDefinitionIgnoredInTransactions() + }, + false + ) + } + + test(s"$prefix: getProcedures") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getProcedures(None, None, None) + yield + val builder = List.newBuilder[String] + while resultSet.next() do + val procedureCat = resultSet.getString("PROCEDURE_CAT") + val procedureSchem = resultSet.getString("PROCEDURE_SCHEM") + val procedureName = resultSet.getString("PROCEDURE_NAME") + val procedureType = resultSet.getShort("PROCEDURE_TYPE") + val specificName = resultSet.getString("SPECIFIC_NAME") + builder += s"$procedureCat, $procedureSchem, $procedureName, $procedureType, $specificName" + builder.result() + }, + List( + "connector_test, null, demoSp, 1, demoSp", + "connector_test, null, func1, 2, func1", + "connector_test, null, func2, 2, func2", + "connector_test, null, getPrice, 2, getPrice", + "connector_test, null, proc1, 1, proc1", + "connector_test, null, proc2, 1, proc2", + "connector_test, null, proc3, 1, proc3", + "connector_test, null, proc4, 1, proc4", + "sys, null, create_synonym_db, 1, create_synonym_db", + "sys, null, diagnostics, 1, diagnostics", + "sys, null, execute_prepared_stmt, 1, execute_prepared_stmt", + "sys, null, extract_schema_from_file_name, 2, extract_schema_from_file_name", + "sys, null, extract_table_from_file_name, 2, extract_table_from_file_name", + "sys, null, format_bytes, 2, format_bytes", + "sys, null, format_path, 2, format_path", + "sys, null, format_statement, 2, format_statement", + "sys, null, format_time, 2, format_time", + "sys, null, list_add, 2, list_add", + "sys, null, list_drop, 2, list_drop", + "sys, null, ps_is_account_enabled, 2, ps_is_account_enabled", + "sys, null, ps_is_consumer_enabled, 2, ps_is_consumer_enabled", + "sys, null, ps_is_instrument_default_enabled, 2, ps_is_instrument_default_enabled", + "sys, null, ps_is_instrument_default_timed, 2, ps_is_instrument_default_timed", + "sys, null, ps_is_thread_instrumented, 2, ps_is_thread_instrumented", + "sys, null, ps_setup_disable_background_threads, 1, ps_setup_disable_background_threads", + "sys, null, ps_setup_disable_consumer, 1, ps_setup_disable_consumer", + "sys, null, ps_setup_disable_instrument, 1, ps_setup_disable_instrument", + "sys, null, ps_setup_disable_thread, 1, ps_setup_disable_thread", + "sys, null, ps_setup_enable_background_threads, 1, ps_setup_enable_background_threads", + "sys, null, ps_setup_enable_consumer, 1, ps_setup_enable_consumer", + "sys, null, ps_setup_enable_instrument, 1, ps_setup_enable_instrument", + "sys, null, ps_setup_enable_thread, 1, ps_setup_enable_thread", + "sys, null, ps_setup_reload_saved, 1, ps_setup_reload_saved", + "sys, null, ps_setup_reset_to_default, 1, ps_setup_reset_to_default", + "sys, null, ps_setup_save, 1, ps_setup_save", + "sys, null, ps_setup_show_disabled, 1, ps_setup_show_disabled", + "sys, null, ps_setup_show_disabled_consumers, 1, ps_setup_show_disabled_consumers", + "sys, null, ps_setup_show_disabled_instruments, 1, ps_setup_show_disabled_instruments", + "sys, null, ps_setup_show_enabled, 1, ps_setup_show_enabled", + "sys, null, ps_setup_show_enabled_consumers, 1, ps_setup_show_enabled_consumers", + "sys, null, ps_setup_show_enabled_instruments, 1, ps_setup_show_enabled_instruments", + "sys, null, ps_statement_avg_latency_histogram, 1, ps_statement_avg_latency_histogram", + "sys, null, ps_thread_account, 2, ps_thread_account", + "sys, null, ps_thread_id, 2, ps_thread_id", + "sys, null, ps_thread_stack, 2, ps_thread_stack", + "sys, null, ps_thread_trx_info, 2, ps_thread_trx_info", + "sys, null, ps_trace_statement_digest, 1, ps_trace_statement_digest", + "sys, null, ps_trace_thread, 1, ps_trace_thread", + "sys, null, ps_truncate_all_tables, 1, ps_truncate_all_tables", + "sys, null, quote_identifier, 2, quote_identifier", + "sys, null, statement_performance_analyzer, 1, statement_performance_analyzer", + "sys, null, sys_get_config, 2, sys_get_config", + "sys, null, table_exists, 1, table_exists", + "sys, null, version_major, 2, version_major", + "sys, null, version_minor, 2, version_minor", + "sys, null, version_patch, 2, version_patch" + ) + ) + } + + test(s"$prefix: getProcedureColumns") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getProcedureColumns(None, None, None, None) + yield + val builder = List.newBuilder[String] + while resultSet.next() do + val procedureCat = resultSet.getString("PROCEDURE_CAT") + val procedureSchem = resultSet.getString("PROCEDURE_SCHEM") + val procedureName = resultSet.getString("PROCEDURE_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val columnType = resultSet.getInt("COLUMN_TYPE") + val dataType = resultSet.getInt("DATA_TYPE") + val typeName = resultSet.getString("TYPE_NAME") + val precision = resultSet.getInt("PRECISION") + val length = resultSet.getInt("LENGTH") + val scale = resultSet.getInt("SCALE") + val radix = resultSet.getInt("RADIX") + val nullable = resultSet.getInt("NULLABLE") + val remarks = resultSet.getString("REMARKS") + val columnDef = resultSet.getString("COLUMN_DEF") + val sqlDataType = resultSet.getInt("SQL_DATA_TYPE") + val sqlDatetimeSub = resultSet.getInt("SQL_DATETIME_SUB") + val charOctetLength = resultSet.getInt("CHAR_OCTET_LENGTH") + val ordinalPosition = resultSet.getInt("ORDINAL_POSITION") + val isNullable = resultSet.getString("IS_NULLABLE") + builder += s"$procedureCat, $procedureSchem, $procedureName, $columnName, $columnType, $dataType, $typeName, $precision, $length, $scale, $radix, $nullable, $remarks, $columnDef, $sqlDataType, $sqlDatetimeSub, $charOctetLength, $ordinalPosition, $isNullable" + builder.result() + }, + List( + "connector_test, null, demoSp, inputParam, 1, 12, VARCHAR, 0, 255, 0, 10, 1, null, null, 0, 0, 1020, 1, YES", + "connector_test, null, demoSp, inOutParam, 2, 4, INT, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 2, YES", + "connector_test, null, func1, , 5, 4, INT, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 0, YES", + "connector_test, null, func2, , 5, 12, VARCHAR, 0, 12, 0, 10, 1, null, null, 0, 0, 48, 0, YES", + "connector_test, null, getPrice, , 5, 4, INT, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 0, YES", + "connector_test, null, getPrice, price, 1, 4, INT, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "connector_test, null, proc2, param, 1, 4, INT, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "connector_test, null, proc3, param1, 1, 4, INT, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "connector_test, null, proc3, param2, 1, 12, VARCHAR, 0, 8, 0, 10, 1, null, null, 0, 0, 32, 2, YES", + "connector_test, null, proc4, param1, 4, 4, INT, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "connector_test, null, proc4, param2, 4, 12, VARCHAR, 0, 8, 0, 10, 1, null, null, 0, 0, 32, 2, YES", + "sys, null, create_synonym_db, in_db_name, 1, 12, VARCHAR, 0, 64, 0, 10, 1, null, null, 0, 0, 256, 1, YES", + "sys, null, create_synonym_db, in_synonym, 1, 12, VARCHAR, 0, 64, 0, 10, 1, null, null, 0, 0, 256, 2, YES", + "sys, null, diagnostics, in_max_runtime, 1, 4, INT UNSIGNED, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, diagnostics, in_interval, 1, 4, INT UNSIGNED, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 2, YES", + "sys, null, diagnostics, in_auto_config, 1, 1, ENUM, 0, 7, 0, 10, 1, null, null, 0, 0, 28, 3, YES", + "sys, null, execute_prepared_stmt, in_query, 1, -1, LONGTEXT, 0, 2147483647, 0, 10, 1, null, null, 0, 0, 2147483647, 1, YES", + "sys, null, extract_schema_from_file_name, , 5, 12, VARCHAR, 0, 64, 0, 10, 1, null, null, 0, 0, 256, 0, YES", + "sys, null, extract_schema_from_file_name, path, 1, 12, VARCHAR, 0, 512, 0, 10, 1, null, null, 0, 0, 2048, 1, YES", + "sys, null, extract_table_from_file_name, , 5, 12, VARCHAR, 0, 64, 0, 10, 1, null, null, 0, 0, 256, 0, YES", + "sys, null, extract_table_from_file_name, path, 1, 12, VARCHAR, 0, 512, 0, 10, 1, null, null, 0, 0, 2048, 1, YES", + "sys, null, format_bytes, , 5, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 0, YES", + "sys, null, format_bytes, bytes, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 1, YES", + "sys, null, format_path, , 5, 12, VARCHAR, 0, 512, 0, 10, 1, null, null, 0, 0, 2048, 0, YES", + "sys, null, format_path, in_path, 1, 12, VARCHAR, 0, 512, 0, 10, 1, null, null, 0, 0, 2048, 1, YES", + "sys, null, format_statement, , 5, -1, LONGTEXT, 0, 2147483647, 0, 10, 1, null, null, 0, 0, 2147483647, 0, YES", + "sys, null, format_statement, statement, 1, -1, LONGTEXT, 0, 2147483647, 0, 10, 1, null, null, 0, 0, 2147483647, 1, YES", + "sys, null, format_time, , 5, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 0, YES", + "sys, null, format_time, picoseconds, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 1, YES", + "sys, null, list_add, , 5, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 0, YES", + "sys, null, list_add, in_list, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 1, YES", + "sys, null, list_add, in_add_value, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 2, YES", + "sys, null, list_drop, , 5, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 0, YES", + "sys, null, list_drop, in_list, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 1, YES", + "sys, null, list_drop, in_drop_value, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 2, YES", + "sys, null, ps_is_account_enabled, , 5, 1, ENUM, 0, 3, 0, 10, 1, null, null, 0, 0, 12, 0, YES", + "sys, null, ps_is_account_enabled, in_host, 1, 12, VARCHAR, 0, 255, 0, 10, 1, null, null, 0, 0, 1020, 1, YES", + "sys, null, ps_is_account_enabled, in_user, 1, 12, VARCHAR, 0, 32, 0, 10, 1, null, null, 0, 0, 128, 2, YES", + "sys, null, ps_is_consumer_enabled, , 5, 1, ENUM, 0, 3, 0, 10, 1, null, null, 0, 0, 12, 0, YES", + "sys, null, ps_is_consumer_enabled, in_consumer, 1, 12, VARCHAR, 0, 64, 0, 10, 1, null, null, 0, 0, 256, 1, YES", + "sys, null, ps_is_instrument_default_enabled, , 5, 1, ENUM, 0, 3, 0, 10, 1, null, null, 0, 0, 12, 0, YES", + "sys, null, ps_is_instrument_default_enabled, in_instrument, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, null, 0, 0, 512, 1, YES", + "sys, null, ps_is_instrument_default_timed, , 5, 1, ENUM, 0, 3, 0, 10, 1, null, null, 0, 0, 12, 0, YES", + "sys, null, ps_is_instrument_default_timed, in_instrument, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, null, 0, 0, 512, 1, YES", + "sys, null, ps_is_thread_instrumented, , 5, 1, ENUM, 0, 7, 0, 10, 1, null, null, 0, 0, 28, 0, YES", + "sys, null, ps_is_thread_instrumented, in_connection_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_setup_disable_consumer, consumer, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, null, 0, 0, 512, 1, YES", + "sys, null, ps_setup_disable_instrument, in_pattern, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, null, 0, 0, 512, 1, YES", + "sys, null, ps_setup_disable_thread, in_connection_id, 1, -5, BIGINT, 19, 19, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_setup_enable_consumer, consumer, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, null, 0, 0, 512, 1, YES", + "sys, null, ps_setup_enable_instrument, in_pattern, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, null, 0, 0, 512, 1, YES", + "sys, null, ps_setup_enable_thread, in_connection_id, 1, -5, BIGINT, 19, 19, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_setup_reset_to_default, in_verbose, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_setup_save, in_timeout, 1, 4, INT, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_setup_show_disabled, in_show_instruments, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_setup_show_disabled, in_show_threads, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 2, YES", + "sys, null, ps_setup_show_enabled, in_show_instruments, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_setup_show_enabled, in_show_threads, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 2, YES", + "sys, null, ps_thread_account, , 5, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 0, YES", + "sys, null, ps_thread_account, in_thread_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_thread_id, , 5, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, null, 0, 0, 0, 0, YES", + "sys, null, ps_thread_id, in_connection_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_thread_stack, , 5, -1, LONGTEXT, 0, 2147483647, 0, 10, 1, null, null, 0, 0, 2147483647, 0, YES", + "sys, null, ps_thread_stack, thd_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_thread_stack, debug, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 2, YES", + "sys, null, ps_thread_trx_info, , 5, -1, LONGTEXT, 0, 2147483647, 0, 10, 1, null, null, 0, 0, 2147483647, 0, YES", + "sys, null, ps_thread_trx_info, in_thread_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_trace_statement_digest, in_digest, 1, 12, VARCHAR, 0, 64, 0, 10, 1, null, null, 0, 0, 256, 1, YES", + "sys, null, ps_trace_statement_digest, in_runtime, 1, 4, INT, 10, 10, 0, 10, 1, null, null, 0, 0, 0, 2, YES", + "sys, null, ps_trace_statement_digest, in_interval, 1, 3, DECIMAL, 2, 2, 2, 10, 1, null, null, 0, 0, 0, 3, YES", + "sys, null, ps_trace_statement_digest, in_start_fresh, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 4, YES", + "sys, null, ps_trace_statement_digest, in_auto_enable, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 5, YES", + "sys, null, ps_trace_thread, in_thread_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, ps_trace_thread, in_outfile, 1, 12, VARCHAR, 0, 255, 0, 10, 1, null, null, 0, 0, 1020, 2, YES", + "sys, null, ps_trace_thread, in_max_runtime, 1, 3, DECIMAL, 20, 20, 2, 10, 1, null, null, 0, 0, 0, 3, YES", + "sys, null, ps_trace_thread, in_interval, 1, 3, DECIMAL, 20, 20, 2, 10, 1, null, null, 0, 0, 0, 4, YES", + "sys, null, ps_trace_thread, in_start_fresh, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 5, YES", + "sys, null, ps_trace_thread, in_auto_setup, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 6, YES", + "sys, null, ps_trace_thread, in_debug, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 7, YES", + "sys, null, ps_truncate_all_tables, in_verbose, 1, -7, BIT, 1, 1, 0, 10, 1, null, null, 0, 0, 0, 1, YES", + "sys, null, quote_identifier, , 5, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 0, YES", + "sys, null, quote_identifier, in_identifier, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, null, 0, 0, 65535, 1, YES", + "sys, null, statement_performance_analyzer, in_action, 1, 1, ENUM, 0, 12, 0, 10, 1, null, null, 0, 0, 48, 1, YES", + "sys, null, statement_performance_analyzer, in_table, 1, 12, VARCHAR, 0, 129, 0, 10, 1, null, null, 0, 0, 516, 2, YES", + "sys, null, statement_performance_analyzer, in_views, 1, 1, SET, 0, 124, 0, 10, 1, null, null, 0, 0, 496, 3, YES", + "sys, null, sys_get_config, , 5, 12, VARCHAR, 0, 128, 0, 10, 1, null, null, 0, 0, 512, 0, YES", + "sys, null, sys_get_config, in_variable_name, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, null, 0, 0, 512, 1, YES", + "sys, null, sys_get_config, in_default_value, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, null, 0, 0, 512, 2, YES", + "sys, null, table_exists, in_db, 1, 12, VARCHAR, 0, 64, 0, 10, 1, null, null, 0, 0, 256, 1, YES", + "sys, null, table_exists, in_table, 1, 12, VARCHAR, 0, 64, 0, 10, 1, null, null, 0, 0, 256, 2, YES", + "sys, null, table_exists, out_exists, 4, 1, ENUM, 0, 10, 0, 10, 1, null, null, 0, 0, 40, 3, YES", + "sys, null, version_major, , 5, -6, TINYINT UNSIGNED, 3, 3, 0, 10, 1, null, null, 0, 0, 0, 0, YES", + "sys, null, version_minor, , 5, -6, TINYINT UNSIGNED, 3, 3, 0, 10, 1, null, null, 0, 0, 0, 0, YES", + "sys, null, version_patch, , 5, -6, TINYINT UNSIGNED, 3, 3, 0, 10, 1, null, null, 0, 0, 0, 0, YES" + ) + ) + } + + test(s"$prefix: getTables") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getTables(Some("world"), None, None, Array.empty[String]) + yield + val builder = List.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val tableType = resultSet.getString("TABLE_TYPE") + val remarks = resultSet.getString("REMARKS") + val typeCat = resultSet.getString("TYPE_CAT") + val typeSchem = resultSet.getString("TYPE_SCHEM") + val typeName = resultSet.getString("TYPE_NAME") + val selfReferencingColName = resultSet.getString("SELF_REFERENCING_COL_NAME") + val refGeneration = resultSet.getString("REF_GENERATION") + builder += s"$tableCat, $tableSchem, $tableName, $tableType, $remarks, $typeCat, $typeSchem, $typeName, $selfReferencingColName, $refGeneration" + builder.result() + }, + List( + "world, null, city, TABLE, , null, null, null, null, null", + "world, null, country, TABLE, , null, null, null, null, null", + "world, null, countrylanguage, TABLE, , null, null, null, null, null", + "world, null, government_office, TABLE, , null, null, null, null, null" + ) + ) + } + + test(s"$prefix: getSchemas") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getSchemas() + yield + val builder = List.newBuilder[String] + while resultSet.next() do builder += resultSet.getString("TABLE_SCHEM") + builder.result() + }, + List.empty + ) + } + + test(s"$prefix: getCatalogs") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getCatalogs() + yield + val builder = List.newBuilder[String] + while resultSet.next() do builder += resultSet.getString("TABLE_CAT") + builder.result() + }, + List("benchmark", "connector_test", "information_schema", "mysql", "performance_schema", "sys", "world", "world2") + ) + } + + test(s"$prefix: getTableTypes") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield + val resultSet = metaData.getTableTypes() + val builder = List.newBuilder[String] + while resultSet.next() do builder += resultSet.getString("TABLE_TYPE") + builder.result() + }, + List( + "LOCAL TEMPORARY", + "SYSTEM TABLE", + "SYSTEM VIEW", + "TABLE", + "VIEW" + ) + ) + } + + test(s"$prefix: getColumns") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getColumns(None, None, Some("tax"), None) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val dataType = resultSet.getInt("DATA_TYPE") + val typeName = resultSet.getString("TYPE_NAME") + val columnSize = resultSet.getInt("COLUMN_SIZE") + val bufferLength = resultSet.getInt("BUFFER_LENGTH") + val decimalDigits = resultSet.getInt("DECIMAL_DIGITS") + val numPrecRadix = resultSet.getInt("NUM_PREC_RADIX") + val nullable = resultSet.getInt("NULLABLE") + val remarks = resultSet.getString("REMARKS") + val columnDef = resultSet.getString("COLUMN_DEF") + val sqlDataType = resultSet.getInt("SQL_DATA_TYPE") + val sqlDatetimeSub = resultSet.getInt("SQL_DATETIME_SUB") + val charOctetLength = resultSet.getInt("CHAR_OCTET_LENGTH") + val ordinalPosition = resultSet.getInt("ORDINAL_POSITION") + val isNullable = resultSet.getString("IS_NULLABLE") + val scopeCatalog = resultSet.getString("SCOPE_CATALOG") + val scopeSchema = resultSet.getString("SCOPE_SCHEMA") + val scopeTable = resultSet.getString("SCOPE_TABLE") + val sourceDataType = resultSet.getShort("SOURCE_DATA_TYPE") + val isAutoincrement = resultSet.getString("IS_AUTOINCREMENT") + builder += s"$tableCat, $tableSchem, $tableName, $columnName, $dataType, $typeName, $columnSize, $bufferLength, $decimalDigits, $numPrecRadix, $nullable, $remarks, $columnDef, $sqlDataType, $sqlDatetimeSub, $charOctetLength, $ordinalPosition, $isNullable, $scopeCatalog, $scopeSchema, $scopeTable, $sourceDataType, $isAutoincrement" + builder.result() + }, + Vector( + "connector_test, null, tax, id, -5, BIGINT, 19, 65535, 0, 10, 0, , null, 0, 0, 0, 1, NO, null, null, null, 0, YES", + "connector_test, null, tax, value, 8, DOUBLE, 22, 65535, 0, 10, 0, , null, 0, 0, 0, 2, NO, null, null, null, 0, NO", + "connector_test, null, tax, start_date, 91, DATE, 10, 65535, 0, 10, 0, , null, 0, 0, 0, 3, NO, null, null, null, 0, NO" + ) + ) + } + + test(s"$prefix: getColumnPrivileges") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getColumnPrivileges(Some("connector_test"), None, Some("tax"), Some("id")) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val grantor = resultSet.getString("GRANTOR") + val grantee = resultSet.getString("GRANTEE") + val privilege = resultSet.getString("PRIVILEGE") + val isGrantable = resultSet.getString("IS_GRANTABLE") + builder += s"$tableCat, $tableSchem, $tableName, $columnName, $grantor, $grantee, $privilege, $isGrantable" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: getTablePrivileges") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getTablePrivileges(None, None, Some("tax")) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val grantor = resultSet.getString("GRANTOR") + val grantee = resultSet.getString("GRANTEE") + val privilege = resultSet.getString("PRIVILEGE") + val isGrantable = resultSet.getString("IS_GRANTABLE") + builder += s"$tableCat, $tableSchem, $tableName, $grantor, $grantee, $privilege, $isGrantable" + builder.result() + }, + Vector.empty + ) + } + + test(s"$prefix: getBestRowIdentifier") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getBestRowIdentifier(None, None, "tax", Some(1), Some(true)) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val scope = resultSet.getShort("SCOPE") + val columnName = resultSet.getString("COLUMN_NAME") + val dataType = resultSet.getInt("DATA_TYPE") + val typeName = resultSet.getString("TYPE_NAME") + val columnSize = resultSet.getInt("COLUMN_SIZE") + val bufferLength = resultSet.getInt("BUFFER_LENGTH") + val decimalDigits = resultSet.getShort("DECIMAL_DIGITS") + val pseudoColumn = resultSet.getShort("PSEUDO_COLUMN") + builder += s"$scope, $columnName, $dataType, $typeName, $columnSize, $bufferLength, $decimalDigits, $pseudoColumn" + builder.result() + }, + Vector("2, id, -5, bigint, 19, 65535, 0, 1") + ) + } + + test(s"$prefix: getVersionColumns") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getVersionColumns(None, None, "tax") + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val scope = resultSet.getShort("SCOPE") + val columnName = resultSet.getString("COLUMN_NAME") + val dataType = resultSet.getInt("DATA_TYPE") + val typeName = resultSet.getString("TYPE_NAME") + val columnSize = resultSet.getInt("COLUMN_SIZE") + val bufferLength = resultSet.getInt("BUFFER_LENGTH") + val decimalDigits = resultSet.getShort("DECIMAL_DIGITS") + val pseudoColumn = resultSet.getShort("PSEUDO_COLUMN") + builder += s"$scope, $columnName, $dataType, $typeName, $columnSize, $bufferLength, $decimalDigits, $pseudoColumn" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: getPrimaryKeys") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getPrimaryKeys(None, None, "tax") + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val keySeq = resultSet.getShort("KEY_SEQ") + val pkName = resultSet.getString("PK_NAME") + builder += s"$tableCat, $tableSchem, $tableName, $columnName, $keySeq, $pkName" + builder.result() + }, + Vector("connector_test, null, tax, id, 1, PRIMARY") + ) + } + + test(s"$prefix: getImportedKeys") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getImportedKeys(None, None, "tax") + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val pkTableCat = resultSet.getString("PKTABLE_CAT") + val pkTableSchem = resultSet.getString("PKTABLE_SCHEM") + val pkTableName = resultSet.getString("PKTABLE_NAME") + val pkColumnName = resultSet.getString("PKCOLUMN_NAME") + val fkTableCat = resultSet.getString("FKTABLE_CAT") + val fkTableSchem = resultSet.getString("FKTABLE_SCHEM") + val fkTableName = resultSet.getString("FKTABLE_NAME") + val fkColumnName = resultSet.getString("FKCOLUMN_NAME") + val keySeq = resultSet.getShort("KEY_SEQ") + val updateRule = resultSet.getShort("UPDATE_RULE") + val deleteRule = resultSet.getShort("DELETE_RULE") + val fkName = resultSet.getString("FK_NAME") + val pkName = resultSet.getString("PK_NAME") + val deferrability = resultSet.getShort("DEFERRABILITY") + builder += s"$pkTableCat, $pkTableSchem, $pkTableName, $pkColumnName, $fkTableCat, $fkTableSchem, $fkTableName, $fkColumnName, $keySeq, $updateRule, $deleteRule, $fkName, $pkName, $deferrability" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: getExportedKeys") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getExportedKeys(None, None, "tax") + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val pkTableCat = resultSet.getString("PKTABLE_CAT") + val pkTableSchem = resultSet.getString("PKTABLE_SCHEM") + val pkTableName = resultSet.getString("PKTABLE_NAME") + val pkColumnName = resultSet.getString("PKCOLUMN_NAME") + val fkTableCat = resultSet.getString("FKTABLE_CAT") + val fkTableSchem = resultSet.getString("FKTABLE_SCHEM") + val fkTableName = resultSet.getString("FKTABLE_NAME") + val fkColumnName = resultSet.getString("FKCOLUMN_NAME") + val keySeq = resultSet.getShort("KEY_SEQ") + val updateRule = resultSet.getShort("UPDATE_RULE") + val deleteRule = resultSet.getShort("DELETE_RULE") + val fkName = resultSet.getString("FK_NAME") + val pkName = resultSet.getString("PK_NAME") + val deferrability = resultSet.getShort("DEFERRABILITY") + builder += s"$pkTableCat, $pkTableSchem, $pkTableName, $pkColumnName, $fkTableCat, $fkTableSchem, $fkTableName, $fkColumnName, $keySeq, $updateRule, $deleteRule, $fkName, $pkName, $deferrability" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: getCrossReference") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getCrossReference(None, None, "tax", None, None, Some("film")) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val pkTableCat = resultSet.getString("PKTABLE_CAT") + val pkTableSchem = resultSet.getString("PKTABLE_SCHEM") + val pkTableName = resultSet.getString("PKTABLE_NAME") + val pkColumnName = resultSet.getString("PKCOLUMN_NAME") + val fkTableCat = resultSet.getString("FKTABLE_CAT") + val fkTableSchem = resultSet.getString("FKTABLE_SCHEM") + val fkTableName = resultSet.getString("FKTABLE_NAME") + val fkColumnName = resultSet.getString("FKCOLUMN_NAME") + val keySeq = resultSet.getShort("KEY_SEQ") + val updateRule = resultSet.getShort("UPDATE_RULE") + val deleteRule = resultSet.getShort("DELETE_RULE") + val fkName = resultSet.getString("FK_NAME") + val pkName = resultSet.getString("PK_NAME") + val deferrability = resultSet.getShort("DEFERRABILITY") + builder += s"$pkTableCat, $pkTableSchem, $pkTableName, $pkColumnName, $fkTableCat, $fkTableSchem, $fkTableName, $fkColumnName, $keySeq, $updateRule, $deleteRule, $fkName, $pkName, $deferrability" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: getIndexInfo") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getIndexInfo(None, None, Some("tax"), false, false) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val nonUnique = resultSet.getBoolean("NON_UNIQUE") + val indexQualifier = resultSet.getString("INDEX_QUALIFIER") + val indexName = resultSet.getString("INDEX_NAME") + val typed = resultSet.getShort("TYPE") + val ordinalPosition = resultSet.getInt("ORDINAL_POSITION") + val columnName = resultSet.getString("COLUMN_NAME") + val ascOrDesc = resultSet.getString("ASC_OR_DESC") + val cardinality = resultSet.getInt("CARDINALITY") + val pages = resultSet.getInt("PAGES") + val filterCondition = resultSet.getString("FILTER_CONDITION") + builder += s"$tableCat, $tableSchem, $tableName, $nonUnique, $indexQualifier, $indexName, $typed, $ordinalPosition, $columnName, $ascOrDesc, $cardinality, $pages, $filterCondition" + builder.result() + }, + Vector( + "connector_test, null, tax, false, null, PRIMARY, 3, 1, id, A, 3, 0, null" + ) + ) + } + + test(s"$prefix: supportsResultSetType") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsResultSetType(ResultSet.TYPE_FORWARD_ONLY) + }, + true + ) + } + + test(s"$prefix: supportsResultSetConcurrency") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsResultSetConcurrency(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY) + }, + true + ) + } + + test(s"$prefix: ownUpdatesAreVisible") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.ownUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY) + }, + false + ) + } + + test(s"$prefix: ownDeletesAreVisible") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.ownDeletesAreVisible(ResultSet.TYPE_FORWARD_ONLY) + }, + false + ) + } + + test(s"$prefix: ownInsertsAreVisible") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.ownInsertsAreVisible(ResultSet.TYPE_FORWARD_ONLY) + }, + false + ) + } + + test(s"$prefix: othersUpdatesAreVisible") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.othersUpdatesAreVisible(ResultSet.TYPE_FORWARD_ONLY) + }, + false + ) + } + + test(s"$prefix: othersDeletesAreVisible") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.othersDeletesAreVisible(ResultSet.TYPE_FORWARD_ONLY) + }, + false + ) + } + + test(s"$prefix: othersInsertsAreVisible") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.othersInsertsAreVisible(ResultSet.TYPE_FORWARD_ONLY) + }, + false + ) + } + + test(s"$prefix: updatesAreDetected") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.updatesAreDetected(ResultSet.TYPE_FORWARD_ONLY) + }, + false + ) + } + + test(s"$prefix: deletesAreDetected") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.deletesAreDetected(ResultSet.TYPE_FORWARD_ONLY) + }, + false + ) + } + + test(s"$prefix: insertsAreDetected") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.insertsAreDetected(ResultSet.TYPE_FORWARD_ONLY) + }, + false + ) + } + + test(s"$prefix: supportsBatchUpdates") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsBatchUpdates() + }, + true + ) + } + + test(s"$prefix: getUDTs") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield + val resultSet = metaData.getUDTs(None, None, None, Array.empty[Int]) + val builder = Vector.newBuilder[String] + while resultSet.next() do + val typeCat = resultSet.getString("TYPE_CAT") + val typeSchem = resultSet.getString("TYPE_SCHEM") + val typeName = resultSet.getString("TYPE_NAME") + val className = resultSet.getString("CLASS_NAME") + val dataType = resultSet.getInt("DATA_TYPE") + val remarks = resultSet.getString("REMARKS") + val baseType = resultSet.getShort("BASE_TYPE") + builder += s"$typeCat, $typeSchem, $typeName, $className, $dataType, $remarks, $baseType" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: supportsSavepoints") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSavepoints() + }, + true + ) + } + + test(s"$prefix: supportsNamedParameters") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsNamedParameters() + }, + false + ) + } + + test(s"$prefix: supportsMultipleOpenResults") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsMultipleOpenResults() + }, + true + ) + } + + test(s"$prefix: supportsGetGeneratedKeys") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsGetGeneratedKeys() + }, + true + ) + } + + test(s"$prefix: getSuperTypes") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield + val resultSet = metaData.getSuperTypes(None, None, None) + val builder = Vector.newBuilder[String] + while resultSet.next() do + val typeCat = resultSet.getString("TYPE_CAT") + val typeSchem = resultSet.getString("TYPE_SCHEM") + val typeName = resultSet.getString("TYPE_NAME") + val superTypeCat = resultSet.getString("SUPERTYPE_CAT") + val superTypeSchem = resultSet.getString("SUPERTYPE_SCHEM") + val superTypeName = resultSet.getString("SUPERTYPE_NAME") + builder += s"$typeCat, $typeSchem, $typeName, $superTypeCat, $superTypeSchem, $superTypeName" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: getSuperTables") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield + val resultSet = metaData.getSuperTables(None, None, None) + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val superTableName = resultSet.getString("SUPERTABLE_NAME") + builder += s"$tableCat, $tableSchem, $tableName, $superTableName" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: getAttributes") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield + val resultSet = metaData.getAttributes(None, None, None, None) + val builder = Vector.newBuilder[String] + while resultSet.next() do + val typeName = resultSet.getString("TYPE_NAME") + val attributeName = resultSet.getString("ATTR_NAME") + val attributeType = resultSet.getString("DATA_TYPE") + val attributeTypeName = resultSet.getString("ATTR_TYPE_NAME") + val attributeSize = resultSet.getInt("ATTR_SIZE") + val decimalDigits = resultSet.getInt("DECIMAL_DIGITS") + val numPrecRadix = resultSet.getInt("NUM_PREC_RADIX") + val nullable = resultSet.getInt("NULLABLE") + val remarks = resultSet.getString("REMARKS") + val attrDef = resultSet.getString("ATTR_DEF") + val sqlDataType = resultSet.getInt("SQL_DATA_TYPE") + val sqlDateTimeSub = resultSet.getInt("SQL_DATETIME_SUB") + val charOctetLength = resultSet.getInt("CHAR_OCTET_LENGTH") + val ordinalPosition = resultSet.getInt("ORDINAL_POSITION") + val isNullable = resultSet.getString("IS_NULLABLE") + builder += s"$typeName, $attributeName, $attributeType, $attributeTypeName, $attributeSize, $decimalDigits, $numPrecRadix, $nullable, $remarks, $attrDef, $sqlDataType, $sqlDateTimeSub, $charOctetLength, $ordinalPosition, $isNullable" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: supportsResultSetHoldability") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsResultSetHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT) + }, + true + ) + } + + test(s"$prefix: getResultSetHoldability") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getResultSetHoldability() + }, + 1 + ) + } + + test(s"$prefix: getDatabaseMajorVersion") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getDatabaseMajorVersion() + }, + 8 + ) + } + + test(s"$prefix: getDatabaseMinorVersion") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getDatabaseMinorVersion() + }, + 4 + ) + } + + test(s"$prefix: getJDBCMajorVersion") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getJDBCMajorVersion() + }, + if prefix == "jdbc" then 4 else 0 + ) + } + + test(s"$prefix: getJDBCMinorVersion") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getJDBCMinorVersion() + }, + if prefix == "jdbc" then 2 else 3 + ) + } + + test(s"$prefix: getSQLStateType") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getSQLStateType() + }, + 2 + ) + } + + test(s"$prefix: locatorsUpdateCopy") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.locatorsUpdateCopy() + }, + true + ) + } + + test(s"$prefix: supportsStatementPooling") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsStatementPooling() + }, + false + ) + } + + test(s"$prefix: getRowIdLifetime") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getRowIdLifetime() + }, + RowIdLifetime.ROWID_UNSUPPORTED + ) + } + + test(s"$prefix: getSchemas") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getSchemas(None, None) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableCatalog = resultSet.getString("TABLE_CATALOG") + builder += s"$tableSchem, $tableCatalog" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: supportsStoredFunctionsUsingCallSyntax") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsStoredFunctionsUsingCallSyntax() + }, + true + ) + } + + test(s"$prefix: autoCommitFailureClosesAllResultSets") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.autoCommitFailureClosesAllResultSets() + }, + false + ) + } + + test(s"$prefix: getClientInfoProperties") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield + val resultSet = metaData.getClientInfoProperties() + val builder = Vector.newBuilder[String] + while resultSet.next() do + val name = resultSet.getString("NAME") + val maxLen = resultSet.getInt("MAX_LEN") + val defaultValue = resultSet.getString("DEFAULT_VALUE") + val description = resultSet.getString("DESCRIPTION") + builder += s"$name, $maxLen, $defaultValue, $description" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: getFunctions") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getFunctions(None, None, None) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val functionCat = resultSet.getString("FUNCTION_CAT") + val functionSchem = resultSet.getString("FUNCTION_SCHEM") + val functionName = resultSet.getString("FUNCTION_NAME") + val functionType = resultSet.getInt("FUNCTION_TYPE") + val specificName = resultSet.getString("SPECIFIC_NAME") + builder += s"$functionCat, $functionSchem, $functionName, $functionType, $specificName" + builder.result() + }, + Vector( + "connector_test, null, func1, 1, func1", + "connector_test, null, func2, 1, func2", + "connector_test, null, getPrice, 1, getPrice", + "sys, null, extract_schema_from_file_name, 1, extract_schema_from_file_name", + "sys, null, extract_table_from_file_name, 1, extract_table_from_file_name", + "sys, null, format_bytes, 1, format_bytes", + "sys, null, format_path, 1, format_path", + "sys, null, format_statement, 1, format_statement", + "sys, null, format_time, 1, format_time", + "sys, null, list_add, 1, list_add", + "sys, null, list_drop, 1, list_drop", + "sys, null, ps_is_account_enabled, 1, ps_is_account_enabled", + "sys, null, ps_is_consumer_enabled, 1, ps_is_consumer_enabled", + "sys, null, ps_is_instrument_default_enabled, 1, ps_is_instrument_default_enabled", + "sys, null, ps_is_instrument_default_timed, 1, ps_is_instrument_default_timed", + "sys, null, ps_is_thread_instrumented, 1, ps_is_thread_instrumented", + "sys, null, ps_thread_account, 1, ps_thread_account", + "sys, null, ps_thread_id, 1, ps_thread_id", + "sys, null, ps_thread_stack, 1, ps_thread_stack", + "sys, null, ps_thread_trx_info, 1, ps_thread_trx_info", + "sys, null, quote_identifier, 1, quote_identifier", + "sys, null, sys_get_config, 1, sys_get_config", + "sys, null, version_major, 1, version_major", + "sys, null, version_minor, 1, version_minor", + "sys, null, version_patch, 1, version_patch" + ) + ) + } + + test(s"$prefix: getFunctionColumns") { + assertIO( + connection.use { conn => + for + metaData <- conn.getMetaData() + resultSet <- metaData.getFunctionColumns(None, None, None, None) + yield + val builder = Vector.newBuilder[String] + while resultSet.next() do + val functionCat = resultSet.getString("FUNCTION_CAT") + val functionSchem = resultSet.getString("FUNCTION_SCHEM") + val functionName = resultSet.getString("FUNCTION_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val columnType = resultSet.getInt("COLUMN_TYPE") + val dataType = resultSet.getInt("DATA_TYPE") + val typeName = resultSet.getString("TYPE_NAME") + val precision = resultSet.getInt("PRECISION") + val length = resultSet.getInt("LENGTH") + val scale = resultSet.getInt("SCALE") + val radix = resultSet.getInt("RADIX") + val nullable = resultSet.getInt("NULLABLE") + val remarks = resultSet.getString("REMARKS") + val charOctetLength = resultSet.getInt("CHAR_OCTET_LENGTH") + val ordinalPosition = resultSet.getInt("ORDINAL_POSITION") + val isNullable = resultSet.getString("IS_NULLABLE") + builder += s"$functionCat, $functionSchem, $functionName, $columnName, $columnType, $dataType, $typeName, $precision, $length, $scale, $radix, $nullable, $remarks, $charOctetLength, $ordinalPosition, $isNullable" + builder.result() + }, + Vector( + "connector_test, null, func1, , 4, 4, INT, 10, 10, 0, 10, 1, null, 0, 0, YES", + "connector_test, null, func2, , 4, 12, VARCHAR, 0, 12, 0, 10, 1, null, 48, 0, YES", + "connector_test, null, getPrice, , 4, 4, INT, 10, 10, 0, 10, 1, null, 0, 0, YES", + "connector_test, null, getPrice, price, 1, 4, INT, 10, 10, 0, 10, 1, null, 0, 1, YES", + "sys, null, extract_schema_from_file_name, , 4, 12, VARCHAR, 0, 64, 0, 10, 1, null, 256, 0, YES", + "sys, null, extract_schema_from_file_name, path, 1, 12, VARCHAR, 0, 512, 0, 10, 1, null, 2048, 1, YES", + "sys, null, extract_table_from_file_name, , 4, 12, VARCHAR, 0, 64, 0, 10, 1, null, 256, 0, YES", + "sys, null, extract_table_from_file_name, path, 1, 12, VARCHAR, 0, 512, 0, 10, 1, null, 2048, 1, YES", + "sys, null, format_bytes, , 4, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 0, YES", + "sys, null, format_bytes, bytes, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 1, YES", + "sys, null, format_path, , 4, 12, VARCHAR, 0, 512, 0, 10, 1, null, 2048, 0, YES", + "sys, null, format_path, in_path, 1, 12, VARCHAR, 0, 512, 0, 10, 1, null, 2048, 1, YES", + "sys, null, format_statement, , 4, -1, LONGTEXT, 0, 2147483647, 0, 10, 1, null, 2147483647, 0, YES", + "sys, null, format_statement, statement, 1, -1, LONGTEXT, 0, 2147483647, 0, 10, 1, null, 2147483647, 1, YES", + "sys, null, format_time, , 4, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 0, YES", + "sys, null, format_time, picoseconds, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 1, YES", + "sys, null, list_add, , 4, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 0, YES", + "sys, null, list_add, in_list, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 1, YES", + "sys, null, list_add, in_add_value, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 2, YES", + "sys, null, list_drop, , 4, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 0, YES", + "sys, null, list_drop, in_list, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 1, YES", + "sys, null, list_drop, in_drop_value, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 2, YES", + "sys, null, ps_is_account_enabled, , 4, 1, ENUM, 0, 3, 0, 10, 1, null, 12, 0, YES", + "sys, null, ps_is_account_enabled, in_host, 1, 12, VARCHAR, 0, 255, 0, 10, 1, null, 1020, 1, YES", + "sys, null, ps_is_account_enabled, in_user, 1, 12, VARCHAR, 0, 32, 0, 10, 1, null, 128, 2, YES", + "sys, null, ps_is_consumer_enabled, , 4, 1, ENUM, 0, 3, 0, 10, 1, null, 12, 0, YES", + "sys, null, ps_is_consumer_enabled, in_consumer, 1, 12, VARCHAR, 0, 64, 0, 10, 1, null, 256, 1, YES", + "sys, null, ps_is_instrument_default_enabled, , 4, 1, ENUM, 0, 3, 0, 10, 1, null, 12, 0, YES", + "sys, null, ps_is_instrument_default_enabled, in_instrument, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, 512, 1, YES", + "sys, null, ps_is_instrument_default_timed, , 4, 1, ENUM, 0, 3, 0, 10, 1, null, 12, 0, YES", + "sys, null, ps_is_instrument_default_timed, in_instrument, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, 512, 1, YES", + "sys, null, ps_is_thread_instrumented, , 4, 1, ENUM, 0, 7, 0, 10, 1, null, 28, 0, YES", + "sys, null, ps_is_thread_instrumented, in_connection_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, 0, 1, YES", + "sys, null, ps_thread_account, , 4, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 0, YES", + "sys, null, ps_thread_account, in_thread_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, 0, 1, YES", + "sys, null, ps_thread_id, , 4, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, 0, 0, YES", + "sys, null, ps_thread_id, in_connection_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, 0, 1, YES", + "sys, null, ps_thread_stack, , 4, -1, LONGTEXT, 0, 2147483647, 0, 10, 1, null, 2147483647, 0, YES", + "sys, null, ps_thread_stack, thd_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, 0, 1, YES", + "sys, null, ps_thread_stack, debug, 1, -7, BIT, 1, 1, 0, 10, 1, null, 0, 2, YES", + "sys, null, ps_thread_trx_info, , 4, -1, LONGTEXT, 0, 2147483647, 0, 10, 1, null, 2147483647, 0, YES", + "sys, null, ps_thread_trx_info, in_thread_id, 1, -5, BIGINT UNSIGNED, 20, 20, 0, 10, 1, null, 0, 1, YES", + "sys, null, quote_identifier, , 4, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 0, YES", + "sys, null, quote_identifier, in_identifier, 1, -1, TEXT, 0, 65535, 0, 10, 1, null, 65535, 1, YES", + "sys, null, sys_get_config, , 4, 12, VARCHAR, 0, 128, 0, 10, 1, null, 512, 0, YES", + "sys, null, sys_get_config, in_variable_name, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, 512, 1, YES", + "sys, null, sys_get_config, in_default_value, 1, 12, VARCHAR, 0, 128, 0, 10, 1, null, 512, 2, YES", + "sys, null, version_major, , 4, -6, TINYINT UNSIGNED, 3, 3, 0, 10, 1, null, 0, 0, YES", + "sys, null, version_minor, , 4, -6, TINYINT UNSIGNED, 3, 3, 0, 10, 1, null, 0, 0, YES", + "sys, null, version_patch, , 4, -6, TINYINT UNSIGNED, 3, 3, 0, 10, 1, null, 0, 0, YES" + ) + ) + } + + test(s"$prefix: getPseudoColumns") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield + val resultSet = metaData.getPseudoColumns(None, None, None, None) + val builder = Vector.newBuilder[String] + while resultSet.next() do + val tableCat = resultSet.getString("TABLE_CAT") + val tableSchem = resultSet.getString("TABLE_SCHEM") + val tableName = resultSet.getString("TABLE_NAME") + val columnName = resultSet.getString("COLUMN_NAME") + val dataType = resultSet.getInt("DATA_TYPE") + val columnSize = resultSet.getInt("COLUMN_SIZE") + val decimalDigits = resultSet.getInt("DECIMAL_DIGITS") + val numPrecRadix = resultSet.getInt("NUM_PREC_RADIX") + val columnUsage = resultSet.getString("COLUMN_USAGE") + val remarks = resultSet.getString("REMARKS") + val charOctetLength = resultSet.getInt("CHAR_OCTET_LENGTH") + val isNullable = resultSet.getInt("IS_NULLABLE") + builder += s"$tableCat, $tableSchem, $tableName, $columnName, $dataType, $columnSize, $decimalDigits, $numPrecRadix, $columnUsage, $remarks, $charOctetLength, $isNullable" + builder.result() + }, + Vector() + ) + } + + test(s"$prefix: generatedKeyAlwaysReturned") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.generatedKeyAlwaysReturned() + }, + true + ) + } + + test(s"$prefix: getMaxLogicalLobSize") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.getMaxLogicalLobSize() + }, + 0L + ) + } + + test(s"$prefix: supportsRefCursors") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsRefCursors() + }, + false + ) + } + + test(s"$prefix: supportsSharding") { + assertIO( + connection.use { conn => + for metaData <- conn.getMetaData() + yield metaData.supportsSharding() + }, + false + ) + }