in modules/db.mysql.parser/src/mysql_parser_module.cpp [1412:2052]
size_t MySQLParserServicesImpl::parseSQLIntoCatalog(MySQLParserContext::Ref context, db_mysql_CatalogRef catalog,
const std::string &sql, grt::DictRef options) {
MySQLParserContextImpl *impl = dynamic_cast<MySQLParserContextImpl *>(context.get());
static std::set<MySQLQueryType> relevantQueryTypes = {
QtAlterDatabase,
QtAlterLogFileGroup,
QtAlterFunction,
QtAlterProcedure,
QtAlterServer,
QtAlterTable,
QtAlterTableSpace,
QtAlterEvent,
QtAlterView,
QtCreateTable,
QtCreateIndex,
QtCreateDatabase,
QtCreateEvent,
QtCreateView,
QtCreateRoutine,
QtCreateProcedure,
QtCreateFunction,
QtCreateUdf,
QtCreateTrigger,
QtCreateLogFileGroup,
QtCreateServer,
QtCreateTableSpace,
QtDropDatabase,
QtDropEvent,
QtDropFunction,
QtDropProcedure,
QtDropIndex,
QtDropLogfileGroup,
QtDropServer,
QtDropTable,
QtDropTablespace,
QtDropTrigger,
QtDropView,
QtRenameTable,
QtUse
};
logDebug2("Parse sql into catalog\n");
bool caseSensitive = impl->caseSensitive;
std::string startSchema = options.get_string("schema");
db_mysql_SchemaRef currentSchema;
if (!startSchema.empty())
currentSchema = ObjectListener::ensureSchemaExists(catalog, startSchema, caseSensitive);
bool defaultSchemaCreated = false;
bool autoGenerateFkNames = options.get_int("gen_fk_names_when_empty") != 0;
// bool reuseExistingObjects = options.get_int("reuse_existing_objects") != 0;
if (!currentSchema.is_valid()) {
currentSchema = db_mysql_SchemaRef::cast_from(catalog->defaultSchema());
if (!currentSchema.is_valid()) {
db_SchemaRef df = find_named_object_in_list(catalog->schemata(), "default_schema", caseSensitive);
if (!df.is_valid())
defaultSchemaCreated = true;
currentSchema = ObjectListener::ensureSchemaExists(catalog, "default_schema", caseSensitive);
}
}
size_t errorCount = 0;
std::vector<StatementRange> ranges;
determineStatementRanges(sql.c_str(), sql.size(), ";", ranges, "\n");
grt::ListRef<GrtObject> createdObjects = grt::ListRef<GrtObject>::cast_from(options.get("created_objects"));
if (!createdObjects.is_valid()) {
createdObjects = grt::ListRef<GrtObject>(grt::Initialized);
options.set("created_objects", createdObjects);
}
StringListRef errors = StringListRef::cast_from(options.get("errors"));
// Collect textual FK references into a local cache. At the end this is used
// to find actual ref tables + columns, when all tables have been parsed.
DbObjectsRefsCache refCache;
for (auto &range : ranges) {
std::string query(sql.c_str() + range.start, range.length);
MySQLQueryType queryType = impl->determineQueryType(query);
if (relevantQueryTypes.count(queryType) == 0)
continue; // Something we are not interested in. Don't bother parsing it.
auto tree = impl->parse(query, MySQLParseUnit::PuGeneric);
if (!impl->errors.empty()) {
errorCount += impl->errors.size();
if (errors.is_valid()) {
for (auto &error : impl->errors)
errors.insert("(" + std::to_string(range.line) + ", " + std::to_string(error.offset) + ") "
+ error.message);
}
continue;
}
auto statementContext = dynamic_cast<MySQLParser::QueryContext *>(tree)->simpleStatement();
switch (queryType) {
case QtCreateDatabase: {
db_mysql_SchemaRef schema(grt::Initialized);
schema->createDate(base::fmttime(0, DATETIME_FMT));
schema->lastChangeDate(schema->createDate());
schema->owner(catalog);
SchemaListener listener(statementContext, catalog, schema, impl->caseSensitive);
schema->oldName(schema->name());
db_SchemaRef existing = find_named_object_in_list(catalog->schemata(), schema->name(), caseSensitive);
if (existing.is_valid()) {
if (!listener.ignoreIfExists) {
catalog->schemata()->remove(existing);
createdObjects.remove_value(existing);
catalog->schemata().insert(schema);
createdObjects.insert(schema);
}
} else {
catalog->schemata().insert(schema);
createdObjects.insert(schema);
}
break;
}
case QtUse: {
std::string schemaName =
base::unquote(statementContext->utilityStatement()->useCommand()->identifier()->getText());
currentSchema = ObjectListener::ensureSchemaExists(catalog, schemaName, caseSensitive);
break;
}
case QtCreateTable: {
db_mysql_TableRef table(grt::Initialized);
table->createDate(base::fmttime(0, DATETIME_FMT));
table->lastChangeDate(table->createDate());
table->owner(currentSchema);
TableListener listener(statementContext, catalog, currentSchema, table, caseSensitive, autoGenerateFkNames,
refCache);
table->oldName(table->name());
db_mysql_SchemaRef schema =
db_mysql_SchemaRef::cast_from(table->owner()); // Might be different from current schema.
// Ignore tables that use a name that is already used for a view (no drop/new-add takes place then).
db_mysql_ViewRef existingView = find_named_object_in_list(schema->views(), table->name());
if (!existingView.is_valid()) {
db_TableRef existingTable = find_named_object_in_list(schema->tables(), table->name());
if (existingTable.is_valid()) {
// Ignore if the table exists already?
if (!listener.ignoreIfExists) {
schema->tables()->remove(existingTable);
createdObjects.remove_value(existingTable);
schema->tables().insert(table);
createdObjects.insert(table);
}
} else {
schema->tables().insert(table);
createdObjects.insert(table);
}
}
break;
}
case QtCreateIndex: {
db_mysql_IndexRef index(grt::Initialized);
index->createDate(base::fmttime(0, DATETIME_FMT));
index->lastChangeDate(index->createDate());
IndexListener listener(statementContext, catalog, currentSchema, index, impl->caseSensitive, refCache);
index->oldName(index->name());
db_TableRef table = db_TableRef::cast_from(index->owner());
if (table.is_valid()) {
db_IndexRef existing = find_named_object_in_list(table->indices(), index->name());
if (existing.is_valid()) {
table->indices()->remove(existing);
createdObjects.remove_value(existing);
}
table->indices().insert(index);
createdObjects.insert(index);
}
break;
}
case QtCreateEvent: {
db_mysql_EventRef event(grt::Initialized);
event->sqlDefinition(base::trim(query));
event->createDate(base::fmttime(0, DATETIME_FMT));
event->lastChangeDate(event->createDate());
event->owner(currentSchema);
EventListener listener(statementContext, catalog, event, impl->caseSensitive);
event->oldName(event->name());
db_mysql_SchemaRef schema =
db_mysql_SchemaRef::cast_from(event->owner()); // Might be different from current schema.
db_EventRef existing = find_named_object_in_list(schema->events(), event->name());
if (existing.is_valid()) {
if (!listener.ignoreIfExists) // Ignore if exists?
{
schema->events()->remove(existing);
createdObjects.remove_value(existing);
schema->events().insert(event);
createdObjects.insert(event);
}
} else {
schema->events().insert(event);
createdObjects.insert(event);
}
break;
}
case QtCreateView: {
db_mysql_ViewRef view(grt::Initialized);
view->sqlDefinition(base::trim(base::trim(query)));
view->createDate(base::fmttime(0, DATETIME_FMT));
view->lastChangeDate(view->createDate());
view->owner(currentSchema);
ViewListener listener(statementContext, catalog, view, caseSensitive);
view->oldName(view->name());
db_mysql_SchemaRef schema =
db_mysql_SchemaRef::cast_from(view->owner()); // Might be different from current schema.
// Ignore views that use a name that is already used for a table (no drop/new-add takes place then).
db_mysql_TableRef existingTable = find_named_object_in_list(schema->tables(), view->name());
if (!existingTable.is_valid()) {
db_mysql_ViewRef existingView = find_named_object_in_list(schema->views(), view->name());
if (existingView.is_valid()) {
schema->views()->remove(existingView);
createdObjects.remove_value(existingView);
}
schema->views().insert(view);
createdObjects.insert(view);
}
break;
}
case QtCreateProcedure:
case QtCreateFunction:
case QtCreateUdf: {
db_mysql_RoutineRef routine(grt::Initialized);
routine->owner(currentSchema);
routine->sqlDefinition(base::trim(query));
routine->createDate(base::fmttime(0, DATETIME_FMT));
routine->lastChangeDate(routine->createDate());
RoutineListener listener(statementContext, catalog, routine, caseSensitive);
routine->oldName(routine->name());
db_mysql_SchemaRef schema =
db_mysql_SchemaRef::cast_from(routine->owner()); // Might be different from current schema.
db_RoutineRef existing = find_named_object_in_list(schema->routines(), routine->name());
if (existing.is_valid()) {
schema->routines()->remove(existing);
createdObjects.remove_value(existing);
}
schema->routines().insert(routine);
createdObjects.insert(routine);
break;
}
case QtCreateTrigger: {
db_mysql_TriggerRef trigger(grt::Initialized);
trigger->sqlDefinition(base::trim(query));
trigger->createDate(base::fmttime(0, DATETIME_FMT));
trigger->lastChangeDate(trigger->createDate());
TriggerListener listener(statementContext, catalog, currentSchema, trigger, caseSensitive);
trigger->oldName(trigger->name());
// It could be the listener had to create stub table. We have to add this to our created objects list.
db_mysql_TableRef table = db_mysql_TableRef::cast_from(trigger->owner());
if (table->isStub())
createdObjects.insert(table);
db_TriggerRef existing = find_named_object_in_list(table->triggers(), trigger->name());
if (existing.is_valid()) {
table->triggers()->remove(existing);
createdObjects.remove_value(existing);
}
table->triggers().insert(trigger);
createdObjects.insert(trigger);
break;
}
case QtCreateLogFileGroup: {
db_mysql_LogFileGroupRef group(grt::Initialized);
group->createDate(base::fmttime(0, DATETIME_FMT));
group->lastChangeDate(group->createDate());
group->owner(catalog);
LogfileGroupListener listener(statementContext, catalog, group, impl->caseSensitive);
group->oldName(group->name());
db_LogFileGroupRef existing = find_named_object_in_list(catalog->logFileGroups(), group->name());
if (existing.is_valid()) {
catalog->logFileGroups()->remove(existing);
createdObjects.remove_value(existing);
}
catalog->logFileGroups().insert(group);
createdObjects.insert(group);
break;
}
case QtCreateServer: {
db_mysql_ServerLinkRef server(grt::Initialized);
server->createDate(base::fmttime(0, DATETIME_FMT));
server->lastChangeDate(server->createDate());
server->owner(catalog);
ServerListener listener(statementContext, catalog, server, impl->caseSensitive);
server->oldName(server->name());
db_ServerLinkRef existing = find_named_object_in_list(catalog->serverLinks(), server->name());
if (existing.is_valid()) {
catalog->serverLinks()->remove(existing);
createdObjects.remove_value(existing);
}
catalog->serverLinks().insert(server);
createdObjects.insert(server);
break;
}
case QtCreateTableSpace: {
db_mysql_TablespaceRef tablespace(grt::Initialized);
tablespace->createDate(base::fmttime(0, DATETIME_FMT));
tablespace->lastChangeDate(tablespace->createDate());
tablespace->owner(catalog);
TablespaceListener listener(statementContext, catalog, tablespace, impl->caseSensitive);
tablespace->oldName(tablespace->name());
db_TablespaceRef existing = find_named_object_in_list(catalog->tablespaces(), tablespace->name());
if (existing.is_valid()) {
catalog->tablespaces()->remove(existing);
createdObjects.remove_value(existing);
}
catalog->tablespaces().insert(tablespace);
createdObjects.insert(tablespace);
break;
}
case QtDropDatabase: {
std::string name = base::unquote(statementContext->dropStatement()->dropDatabase()->schemaRef()->getText());
db_SchemaRef schema = find_named_object_in_list(catalog->schemata(), name);
if (schema.is_valid()) {
catalog->schemata()->remove(schema);
createdObjects.remove_value(schema);
if (catalog->defaultSchema() == schema)
catalog->defaultSchema(db_mysql_SchemaRef());
if (currentSchema == schema)
currentSchema = db_mysql_SchemaRef::cast_from(catalog->defaultSchema());
if (!currentSchema.is_valid())
currentSchema = ObjectListener::ensureSchemaExists(catalog, "default_schema", caseSensitive);
}
break;
}
case QtDropEvent: {
IdentifierListener listener(statementContext->dropStatement()->dropEvent()->eventRef());
db_SchemaRef schema = currentSchema;
if (listener.parts.size() > 1 && !listener.parts[0].empty())
schema = ObjectListener::ensureSchemaExists(catalog, listener.parts[0], caseSensitive);
db_EventRef event = find_named_object_in_list(schema->events(), listener.parts.back());
if (event.is_valid()) {
schema->events()->remove(event);
createdObjects.remove_value(event);
}
break;
}
case QtDropProcedure:
case QtDropFunction: // Including UDFs.
{
tree::ParseTree *nameContext;
if (queryType == QtDropFunction)
nameContext = statementContext->dropStatement()->dropFunction()->functionRef();
else
nameContext = statementContext->dropStatement()->dropProcedure()->procedureRef();
IdentifierListener listener(nameContext);
db_SchemaRef schema = currentSchema;
if (listener.parts.size() > 1 && !listener.parts[0].empty())
schema = ObjectListener::ensureSchemaExists(catalog, listener.parts[0], caseSensitive);
db_RoutineRef routine = find_named_object_in_list(schema->routines(), listener.parts.back());
if (routine.is_valid()) {
schema->routines()->remove(routine);
createdObjects.remove_value(routine);
}
break;
}
case QtDropIndex: {
std::string name;
{
IdentifierListener listener(statementContext->dropStatement()->dropIndex()->indexRef());
name = listener.parts.back();
}
IdentifierListener listener(statementContext->dropStatement()->dropIndex()->tableRef());
db_SchemaRef schema = currentSchema;
if (listener.parts.size() > 1 && !listener.parts[0].empty())
schema = ObjectListener::ensureSchemaExists(catalog, listener.parts[0], caseSensitive);
db_TableRef table = find_named_object_in_list(schema->tables(), listener.parts.back());
if (table.is_valid()) {
db_IndexRef index = find_named_object_in_list(table->indices(), name);
if (index.is_valid()) {
table->indices()->remove(index);
createdObjects.remove_value(index);
}
}
break;
}
case QtDropLogfileGroup: {
IdentifierListener listener(statementContext->dropStatement()->dropLogfileGroup()->logfileGroupRef());
db_LogFileGroupRef group = find_named_object_in_list(catalog->logFileGroups(), listener.parts.back());
if (group.is_valid()) {
catalog->logFileGroups()->remove(group);
createdObjects.remove_value(group);
}
break;
}
case QtDropServer: {
IdentifierListener listener(statementContext->dropStatement()->dropServer()->serverRef());
db_ServerLinkRef server = find_named_object_in_list(catalog->serverLinks(), listener.parts.back());
if (server.is_valid()) {
catalog->serverLinks()->remove(server);
createdObjects.remove_value(server);
}
break;
}
case QtDropTable: {
// We can have a list of tables to drop here.
for (auto tableRef : statementContext->dropStatement()->dropTable()->tableRefList()->tableRef()) {
IdentifierListener listener(tableRef);
db_SchemaRef schema = currentSchema;
if (listener.parts.size() > 1 && !listener.parts[0].empty())
schema = ObjectListener::ensureSchemaExists(catalog, listener.parts[0], caseSensitive);
db_TableRef table = find_named_object_in_list(schema->tables(), listener.parts.back());
if (table.is_valid()) {
schema->tables()->remove(table);
createdObjects.remove_value(table);
}
}
break;
}
case QtDropView: {
// We can have a list of views to drop here.
for (auto tableRef : statementContext->dropStatement()->dropView()->viewRefList()->viewRef()) {
IdentifierListener listener(tableRef);
db_SchemaRef schema = currentSchema;
if (listener.parts.size() > 1 && !listener.parts[0].empty())
schema = ObjectListener::ensureSchemaExists(catalog, listener.parts[0], caseSensitive);
db_ViewRef view = find_named_object_in_list(schema->views(), listener.parts.back());
if (view.is_valid()) {
schema->views()->remove(view);
createdObjects.remove_value(view);
}
}
break;
}
case QtDropTablespace: {
IdentifierListener listener(statementContext->dropStatement()->dropTableSpace()->tablespaceRef());
db_TablespaceRef tablespace = find_named_object_in_list(catalog->tablespaces(), listener.parts.back());
if (tablespace.is_valid()) {
catalog->tablespaces()->remove(tablespace);
createdObjects.remove_value(tablespace);
}
break;
}
case QtDropTrigger: {
IdentifierListener listener(statementContext->dropStatement()->dropTrigger()->triggerRef());
// Even though triggers are schema level objects they work on specific tables
// and that's why we store them under the affected tables, not in the schema object.
// This however makes it more difficult to find the trigger to delete, as we have to
// iterate over all tables.
db_SchemaRef schema = currentSchema;
if (listener.parts.size() > 1 && !listener.parts[0].empty())
schema = ObjectListener::ensureSchemaExists(catalog, listener.parts[0], caseSensitive);
for (auto table : schema->tables()) {
db_TriggerRef trigger = find_named_object_in_list(table->triggers(), listener.parts.back());
if (trigger.is_valid()) {
table->triggers()->remove(trigger);
createdObjects.remove_value(trigger);
break; // A trigger can only be assigned to a single table, so we can stop here.
}
}
break;
}
case QtRenameTable: {
// Renaming a table is special as you can use it also to rename a view and to move
// a table from one schema to the other (not for views, though).
// Due to the way we store triggers we have an easy life wrt. related triggers.
for (auto renamePair : statementContext->renameTableStatement()->renamePair()) {
IdentifierListener sourceListener(renamePair->tableRef());
db_SchemaRef sourceSchema = currentSchema;
if (sourceListener.parts.size() > 1 && !sourceListener.parts[0].empty())
sourceSchema = ObjectListener::ensureSchemaExists(catalog, sourceListener.parts[0], caseSensitive);
IdentifierListener targetListener(renamePair->tableName());
db_SchemaRef targetSchema = currentSchema;
if (targetListener.parts.size() > 1 && !targetListener.parts[0].empty())
targetSchema = ObjectListener::ensureSchemaExists(catalog, targetListener.parts[0], caseSensitive);
db_ViewRef view = find_named_object_in_list(sourceSchema->views(), sourceListener.parts.back());
if (view.is_valid()) {
// Cannot move between schemas.
if (sourceSchema == targetSchema)
view->name(targetListener.parts.back());
} else {
// Renaming a table.
db_TableRef table = find_named_object_in_list(sourceSchema->tables(), sourceListener.parts.back());
if (table.is_valid()) {
if (sourceSchema != targetSchema) {
sourceSchema->tables()->remove(table);
targetSchema->tables().insert(table);
createdObjects.insert(table);
}
table->name(targetListener.parts.back());
}
}
}
break;
}
// Alter commands. At the moment we only support a limited number of cases as we mostly
// need SQL-to-GRT conversion for create scripts.
case QtAlterDatabase: {
IdentifierListener listener(statementContext->alterStatement()->alterDatabase()->schemaRef());
db_mysql_SchemaRef schema = ObjectListener::ensureSchemaExists(catalog, listener.parts.back(), caseSensitive);
schema->lastChangeDate(base::fmttime(0, DATETIME_FMT));
SchemaListener(statementContext, catalog, schema, impl->caseSensitive);
break;
}
case QtAlterLogFileGroup:
break;
case QtAlterFunction:
break;
case QtAlterProcedure:
break;
case QtAlterServer:
break;
case QtAlterTable: { // Alter table only for adding/removing indices and for renames.
IdentifierListener listener(statementContext->alterStatement()->alterTable()->tableRef());
db_mysql_SchemaRef schema = currentSchema;
if (listener.parts.size() > 1 && !listener.parts[0].empty())
schema = ObjectListener::ensureSchemaExists(catalog, listener.parts[0], caseSensitive);
db_mysql_TableRef table = find_named_object_in_list(schema->tables(), listener.parts.back(), caseSensitive);
if (table.is_valid())
TableAlterListener(statementContext, catalog, table, caseSensitive, autoGenerateFkNames, refCache);
else {
db_mysql_ViewRef view = find_named_object_in_list(schema->views(), listener.parts.back(), caseSensitive);
if (view.is_valid())
TableAlterListener(statementContext, catalog, view, caseSensitive, autoGenerateFkNames, refCache);
}
break;
}
case QtAlterTableSpace:
break;
case QtAlterEvent:
break;
case QtAlterView:
break;
default:
continue; // Ignore anything else.
}
}
resolveReferences(catalog, refCache, context->isCaseSensitive());
// Remove the default_schema we may have created at the start, if it is empty.
if (defaultSchemaCreated) {
currentSchema = ObjectListener::ensureSchemaExists(catalog, "default_schema", caseSensitive);
if (currentSchema->tables().count() == 0 && currentSchema->views().count() == 0 &&
currentSchema->routines().count() == 0 && currentSchema->synonyms().count() == 0 &&
currentSchema->sequences().count() == 0 && currentSchema->events().count() == 0)
catalog->schemata().remove_value(currentSchema);
}
return errorCount;
}