public function parseColumnDefinitions()
{
$this->skipSpaces();
$tableDef = new stdClass();
$tableDef->columns = [];
while (!$this->metEnd()) {
while (!$this->metEnd()) {
$tryPos = $this->p;
$foundTableConstraint = $this->tryParseTableConstraints();
$this->rollback($tryPos);
if ($foundTableConstraint) {
break;
}
$identifier = $this->tryParseIdentifier();
if (!$identifier) {
break;
}
$column = new stdClass();
$column->name = $identifier->val;
$this->skipSpaces();
$typeName = $this->tryParseTypeName();
if ($typeName) {
$this->skipSpaces();
$column->type = $typeName->val;
$precision = $this->tryParseTypePrecision();
if ($precision && $precision->val) {
if (count($precision->val) == 2) {
$column->length = $precision->val[0];
$column->decimals = $precision->val[1];
} elseif (count($precision->val) == 1) {
$column->length = $precision->val[0];
}
}
if (in_array(strtoupper($column->type), self::$intTypes)) {
$unsigned = $this->consume('unsigned', 'unsigned');
}
while ($constraintToken = $this->tryParseColumnConstraint()) {
if ($constraintToken->val == 'PRIMARY') {
$this->tryParseKeyword(['KEY']);
$column->primary = true;
if ($orderingToken = $this->tryParseKeyword(['ASC', 'DESC'])) {
$column->ordering = $orderingToken->val;
}
if ($this->tryParseKeyword(['AUTOINCREMENT'])) {
$column->autoIncrement = true;
}
} elseif ($constraintToken->val == 'UNIQUE') {
$column->unique = true;
} elseif ($constraintToken->val == 'NOT NULL') {
$column->notNull = true;
} elseif ($constraintToken->val == 'NULL') {
$column->notNull = false;
} elseif ($constraintToken->val == 'DEFAULT') {
// parse scalar
if ($scalarToken = $this->tryParseScalar()) {
$column->default = $scalarToken->val;
} elseif ($literal = $this->tryParseKeyword(['CURRENT_TIME', 'CURRENT_DATE', 'CURRENT_TIMESTAMP'], 'literal')) {
$column->default = $literal;
} elseif ($null = $this->tryParseKeyword(['NULL'])) {
$column->default = null;
} elseif ($null = $this->tryParseKeyword(['TRUE'])) {
$column->default = true;
} elseif ($null = $this->tryParseKeyword(['FALSE'])) {
$column->default = false;
} else {
throw new Exception("Can't parse literal: " . $this->currentWindow());
}
} elseif ($constraintToken->val == 'COLLATE') {
$collateName = $this->tryParseIdentifier();
$column->collate = $collateName->val;
} elseif ($constraintToken->val == 'REFERENCES') {
$tableNameToken = $this->tryParseIdentifier();
$this->advance('(');
$columnNames = $this->parseColumnNames();
$this->advance(')');
$actions = [];
if ($this->tryParseKeyword(['ON'])) {
while ($onToken = $this->tryParseKeyword(['DELETE', 'UPDATE'])) {
$on = $onToken->val;
$actionToken = $this->tryParseKeyword(['SET NULL', 'SET DEFAULT', 'CASCADE', 'RESTRICT', 'NO ACTION']);
$actions[$on] = $actionToken->val;
}
}
$column->references = (object) array('table' => $tableNameToken->val, 'columns' => $columnNames, 'actions' => $actions);
}
$this->skipSpaces();
}
}
$this->skipSpaces();
if ($this->metComma()) {
$this->skipComma();
$this->skipSpaces();
$tableDef->columns[] = $column;
} else {
$tableDef->columns[] = $column;
}
}
while (!$this->metEnd() && ($tableConstraints = $this->tryParseTableConstraints())) {
$tableDef->tableConstraints = $tableConstraints;
$this->skipSpaces();
if ($this->metComma()) {
$this->skipComma();
$this->skipSpaces();
}
}
$this->advance();
}
return $tableDef;
}