Skip to content

Commit

Permalink
MDL-31899 use information_schema for normal db tables in get_columns()
Browse files Browse the repository at this point in the history
This change is based on patch by Charles Fulton.
  • Loading branch information
skodak committed Mar 10, 2012
1 parent f886a2c commit bc09aa1
Showing 1 changed file with 185 additions and 104 deletions.
289 changes: 185 additions & 104 deletions lib/dml/mysqli_native_moodle_database.php
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,12 @@ public function get_columns($table, $usecache=true) {

$this->columns[$table] = array();

$sql = "SHOW COLUMNS FROM {$this->prefix}$table";
$sql = "SELECT column_name, data_type, character_maximum_length, numeric_precision,
numeric_scale, is_nullable, column_type, column_default, column_key, extra
FROM information_schema.columns
WHERE table_name = '" . $this->prefix.$table . "'
AND table_schema = '" . $this->dbname . "'
ORDER BY ordinal_position";
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end(true); // Don't want to throw anything here ever. MDL-30147
Expand All @@ -448,118 +453,194 @@ public function get_columns($table, $usecache=true) {
return array();
}

while ($rawcolumn = $result->fetch_assoc()) {
$rawcolumn = (object)array_change_key_case($rawcolumn, CASE_LOWER);

$info = new stdClass();
$info->name = $rawcolumn->field;
$matches = null;

if (preg_match('/(enum|varchar)\((\d+)\)/i', $rawcolumn->type, $matches)) {
$info->type = 'varchar';
$info->meta_type = 'C';
$info->max_length = $matches[2];
$info->scale = null;
$info->not_null = ($rawcolumn->null === 'NO');
$info->default_value = $rawcolumn->default;
$info->has_default = is_null($info->default_value) ? false : true;
$info->primary_key = ($rawcolumn->key === 'PRI');
$info->binary = false;
$info->unsigned = null;
$info->auto_increment= false;
$info->unique = null;

} else if (preg_match('/([a-z]*int[a-z]*)\((\d+)\)/i', $rawcolumn->type, $matches)) {
$info->type = $matches[1];
$info->primary_key = ($rawcolumn->key === 'PRI');
if ($info->primary_key) {
$info->meta_type = 'R';
$info->max_length = $matches[2];
$info->scale = null;
$info->not_null = ($rawcolumn->null === 'NO');
$info->default_value = $rawcolumn->default;
$info->has_default = is_null($info->default_value) ? false : true;
$info->binary = false;
$info->unsigned = (stripos($rawcolumn->type, 'unsigned') !== false);
$info->auto_increment= true;
$info->unique = true;
if ($result->num_rows > 0) {
// standard table exists
while ($rawcolumn = $result->fetch_assoc()) {
$info = (object)$this->get_column_info((object)$rawcolumn);
$this->columns[$table][$info->name] = new database_column_info($info);
}
$result->close();

} else {
// temporary tables are not in information schema, let's try it the old way
$result->close();
$sql = "SHOW COLUMNS FROM {$this->prefix}$table";
$this->query_start($sql, null, SQL_QUERY_AUX);
$result = $this->mysqli->query($sql);
$this->query_end(true);
if ($result === false) {
return array();
}
while ($rawcolumn = $result->fetch_assoc()) {
$rawcolumn = (object)array_change_key_case($rawcolumn, CASE_LOWER);
$rawcolumn->column_name = $rawcolumn->field; unset($rawcolumn->field);
$rawcolumn->column_type = $rawcolumn->type; unset($rawcolumn->type);
$rawcolumn->character_maximum_length = null;
$rawcolumn->numeric_precision = null;
$rawcolumn->numeric_scale = null;
$rawcolumn->is_nullable = $rawcolumn->null; unset($rawcolumn->null);
$rawcolumn->column_default = $rawcolumn->default; unset($rawcolumn->default);
$rawcolumn->column_key = $rawcolumn->key; unset($rawcolumn->default);
$rawcolumn->extra = ($rawcolumn->column_name === 'id') ? 'auto_increment' : '';

if (preg_match('/(enum|varchar)\((\d+)\)/i', $rawcolumn->column_type, $matches)) {
$rawcolumn->data_type = $matches[1];
$rawcolumn->character_maximum_length = $matches[2];

} else if (preg_match('/([a-z]*int[a-z]*)\((\d+)\)/i', $rawcolumn->column_type, $matches)) {
$rawcolumn->data_type = $matches[1];
$rawcolumn->character_maximum_length = $matches[2];

} else if (preg_match('/(decimal)\((\d+),(\d+)\)/i', $rawcolumn->column_type, $matches)) {
$rawcolumn->data_type = $matches[1];
$rawcolumn->numeric_precision = $matches[2];
$rawcolumn->numeric_scale = $matches[3];

} else if (preg_match('/(double|float)(\((\d+),(\d+)\))?/i', $rawcolumn->column_type, $matches)) {
$rawcolumn->data_type = $matches[1];
$rawcolumn->numeric_precision = isset($matches[3]) ? $matches[3] : null;
$rawcolumn->numeric_scale = isset($matches[4]) ? $matches[4] : null;

} else if (preg_match('/([a-z]*text)/i', $rawcolumn->column_type, $matches)) {
$rawcolumn->data_type = $matches[1];
$rawcolumn->character_maximum_length = -1; // unknown

} else if (preg_match('/([a-z]*blob)/i', $rawcolumn->column_type, $matches)) {
$rawcolumn->data_type = $matches[1];

} else {
$info->meta_type = 'I';
$info->max_length = $matches[2];
$info->scale = null;
$info->not_null = ($rawcolumn->null === 'NO');
$info->default_value = $rawcolumn->default;
$info->has_default = is_null($info->default_value) ? false : true;
$info->binary = false;
$info->unsigned = (stripos($rawcolumn->type, 'unsigned') !== false);
$info->auto_increment= false;
$info->unique = null;
$rawcolumn->data_type = $rawcolumn->column_type;
}

} else if (preg_match('/(decimal)\((\d+),(\d+)\)/i', $rawcolumn->type, $matches)) {
$info->type = $matches[1];
$info->meta_type = 'N';
$info->max_length = $matches[2];
$info->scale = $matches[3];
$info->not_null = ($rawcolumn->null === 'NO');
$info->default_value = $rawcolumn->default;
$info->has_default = is_null($info->default_value) ? false : true;
$info->primary_key = ($rawcolumn->key === 'PRI');
$info->binary = false;
$info->unsigned = (stripos($rawcolumn->type, 'unsigned') !== false);
$info->auto_increment= false;
$info->unique = null;

} else if (preg_match('/(double|float)(\((\d+),(\d+)\))?/i', $rawcolumn->type, $matches)) {
$info->type = $matches[1];
$info->meta_type = 'N';
$info->max_length = isset($matches[3]) ? $matches[3] : null;
$info->scale = isset($matches[4]) ? $matches[4] : null;
$info->not_null = ($rawcolumn->null === 'NO');
$info->default_value = $rawcolumn->default;
$info->has_default = is_null($info->default_value) ? false : true;
$info->primary_key = ($rawcolumn->key === 'PRI');
$info->binary = false;
$info->unsigned = (stripos($rawcolumn->type, 'unsigned') !== false);
$info->auto_increment= false;
$info->unique = null;

} else if (preg_match('/([a-z]*text)/i', $rawcolumn->type, $matches)) {
$info->type = $matches[1];
$info->meta_type = 'X';
$info->max_length = -1;
$info->scale = null;
$info->not_null = ($rawcolumn->null === 'NO');
$info->default_value = $rawcolumn->default;
$info->has_default = is_null($info->default_value) ? false : true;
$info->primary_key = ($rawcolumn->key === 'PRI');
$info->binary = false;
$info->unsigned = null;
$info->auto_increment= false;
$info->unique = null;

} else if (preg_match('/([a-z]*blob)/i', $rawcolumn->type, $matches)) {
$info->type = $matches[1];
$info->meta_type = 'B';
$info->max_length = -1;
$info->scale = null;
$info->not_null = ($rawcolumn->null === 'NO');
$info->default_value = $rawcolumn->default;
$info->has_default = is_null($info->default_value) ? false : true;
$info->primary_key = false;
$info->binary = true;
$info->unsigned = null;
$info->auto_increment= false;
$info->unique = null;
$info = $this->get_column_info($rawcolumn);
$this->columns[$table][$info->name] = new database_column_info($info);
}
$result->close();
}

return $this->columns[$table];
}

/**
* Returns moodle column info for raw column from information schema.
* @param stdClass $rawcolumn
* @return stdClass standardised colum info
*/
private function get_column_info(stdClass $rawcolumn) {
$rawcolumn = (object)$rawcolumn;
$info = new stdClass();
$info->name = $rawcolumn->column_name;
$info->type = $rawcolumn->data_type;
$info->meta_type = $this->mysqltype2moodletype($rawcolumn->data_type);
$info->default_value = $rawcolumn->column_default;
$info->has_default = !is_null($rawcolumn->column_default);
$info->not_null = ($rawcolumn->is_nullable === 'NO');
$info->primary_key = ($rawcolumn->column_key === 'PRI');
$info->binary = false;
$info->unsigned = null;
$info->auto_increment = false;
$info->unique = null;
$info->scale = null;

if ($info->meta_type === 'C') {
$info->max_length = $rawcolumn->character_maximum_length;

} else if ($info->meta_type === 'I') {
if ($info->primary_key) {
$info->meta_type = 'R';
$info->unique = true;
}
$info->max_length = $rawcolumn->numeric_precision;
$info->unsigned = (stripos($rawcolumn->column_type, 'unsigned') !== false);
$info->auto_increment= (strpos($rawcolumn->extra, 'auto_increment') !== false);

} else if ($info->meta_type === 'N') {
$info->max_length = $rawcolumn->numeric_precision;
$info->scale = $rawcolumn->numeric_scale;
$info->unsigned = (stripos($rawcolumn->column_type, 'unsigned') !== false);

} else if ($info->meta_type === 'X') {
if ("$rawcolumn->character_maximum_length" === '4294967295') { // watch out for PHP max int limits!
// means maximum moodle size for text column, in other drivers it may also mean unknown size
$info->max_length = -1;
} else {
$info->max_length = $rawcolumn->character_maximum_length;
}
$info->primary_key = false;

$this->columns[$table][$info->name] = new database_column_info($info);
} else if ($info->meta_type === 'B') {
$info->max_length = -1;
$info->primary_key = false;
$info->binary = true;
}

$result->close();
return $info;
}

return $this->columns[$table];
/**
* Normalise column type.
* @param string $mysql_type
* @return string one character
* @throws dml_exception
*/
private function mysqltype2moodletype($mysql_type) {
$type = null;

switch(strtoupper($mysql_type)) {
case 'BIT':
$type = 'L';
break;

case 'TINYINT':
case 'SMALLINT':
case 'MEDIUMINT':
case 'INT':
case 'BIGINT':
$type = 'I';
break;

case 'FLOAT':
case 'DOUBLE':
case 'DECIMAL':
$type = 'N';
break;

case 'CHAR':
case 'ENUM':
case 'SET':
case 'VARCHAR':
$type = 'C';
break;

case 'TINYTEXT':
case 'TEXT':
case 'MEDIUMTEXT':
case 'LONGTEXT':
$type = 'X';
break;

case 'BINARY':
case 'VARBINARY':
case 'BLOB':
case 'TINYBLOB':
case 'MEDIUMBLOB':
case 'LONGBLOB':
$type = 'B';
break;

case 'DATE':
case 'TIME':
case 'DATETIME':
case 'TIMESTAMP':
case 'YEAR':
$type = 'D';
break;
}

if (!$type) {
throw new dml_exception('invalidmysqlnativetype', $mysql_type);
}
return $type;
}

/**
Expand Down

0 comments on commit bc09aa1

Please sign in to comment.