in sources/cg_schema.c [1047:1221]
static void cg_schema_manage_recreate_tables(
charbuf *output,
charbuf *decls,
recreate_annotation *notes,
size_t count)
{
Contract(notes);
Contract(count);
CHARBUF_OPEN(recreate);
CHARBUF_OPEN(update_tables);
CHARBUF_OPEN(pending_table_creates);
// non-null-callbacks will generate SQL for Sqlite (no attributes)
gen_sql_callbacks callbacks;
init_gen_sql_callbacks(&callbacks);
callbacks.mode = gen_mode_no_annotations;
crc_t table_crc = 0;
for (size_t i = 0; i < count; i++) {
recreate_annotation *note = ¬es[i];
EXTRACT_NOTNULL(recreate_attr, note->annotation_ast);
EXTRACT(delete_attr, recreate_attr->right);
// this coveres either deleted or unsubscribed
ast_node *ast = note->target_ast;
ast_node *ast_output = ast;
bool_t deleted = is_deleted(ast);
Invariant(is_ast_create_table_stmt(ast));
bool_t is_eponymous = false;
if (is_virtual_ast(ast)) {
ast_output = ast->parent;
Invariant(is_ast_create_virtual_table_stmt(ast_output));
EXTRACT_NOTNULL(module_info, ast_output->left);
EXTRACT_NOTNULL(create_table_name_flags, ast->left);
EXTRACT_NOTNULL(table_flags_attrs, create_table_name_flags->left);
EXTRACT_OPTION(flags, table_flags_attrs->left);
is_eponymous = !!(flags & VTAB_IS_EPONYMOUS);
}
if (!include_from_region(ast->sem->region, SCHEMA_TO_UPGRADE)) {
continue;
}
if (is_eponymous) {
// eponymous virtual tables do not get created or deleted
continue;
}
EXTRACT_NOTNULL(create_table_name_flags, ast->left);
EXTRACT_NOTNULL(table_flags_attrs, create_table_name_flags->left);
EXTRACT_STRING(table_name, create_table_name_flags->right);
// recreate if needed
CHARBUF_OPEN(make_table);
if (!deleted) {
gen_set_output_buffer(&make_table);
gen_statement_with_callbacks(ast_output, &callbacks);
bprintf(&make_table, ";\n");
}
// note that this will also drop any indices that are on the table
bprintf(&update_tables, " DROP TABLE IF EXISTS %s;\n", table_name);
list_item *index_list = ast->sem->index_list;
// now create the various indices but not the deleted ones
for (list_item *item = index_list; item; item = item->next) {
ast_node *index = item->ast;
// deleted index, don't recreate it
if (index->sem->delete_version > 0) {
continue;
}
gen_statement_with_callbacks(index, &callbacks);
bprintf(&make_table, ";\n");
}
table_crc ^= crc_charbuf(&make_table);
// Now we have to remember that the tables in the recreate annotations have been
// sorted by reverse ordinal, meaning they are in the correct order to DROP
// we emit the DROP statements first as normal but the creates have to be
// stashed in a pending buffer that accumulates in the reverse order. When we're
// done with the group, then we emit the whole batch of creates in the natural order.
// This is done in one pass because there could be filtering and whatnot and so
// this way we know we get exactly the right tables. It does mean some buffer
// shuffling.
// This only matters for recreate groups, with none-groups this is a big no-op
// Note also that CQL0060 prevents anyone from taking an FK on a table that is
// recreate and either not in a group at all or in a different group. So only
// the tables in the processed group could have FKs and those are handled correctly here.
CHARBUF_OPEN(temp);
bindent(&temp, &make_table, 4);
bprintf(&temp, "%s", pending_table_creates.ptr);
bclear(&pending_table_creates);
bprintf(&pending_table_creates, "%s", temp.ptr);
CHARBUF_CLOSE(temp);
CHARBUF_CLOSE(make_table);
CSTR gname = note->group_name;
// if there is a group and and this node can be merged with the next
// then hold the update and accumulate the CRC
if (i + 1 < count && gname[0] && !Strcasecmp(gname, (note+1)->group_name)) {
continue;
}
bprintf(&update_tables, "%s", pending_table_creates.ptr);
bclear(&pending_table_creates);
CHARBUF_OPEN(facet);
CSTR migrate_key = NULL;
if (gname[0]) {
// we're updating the whole group
bprintf(&facet, "%s_group_crc", gname);
migrate_key = gname;
}
else {
bprintf(&facet, "%s_table_crc", table_name);
migrate_key = table_name;
}
ast_node *migration = find_recreate_migrator(migrate_key);
if (migration) {
EXTRACT_STRING(proc, migration->right);
CHARBUF_OPEN(migrate_table);
bprintf(&migrate_table, "\n -- recreate migration procedure required\n");
bprintf(&migrate_table, " CALL %s();\n\n", proc);
bprintf(&update_tables, migrate_table.ptr);
bprintf(decls, "DECLARE PROC %s() USING TRANSACTION;\n", proc);
table_crc ^= crc_charbuf(&migrate_table);
CHARBUF_CLOSE(migrate_table);
}
bprintf(&recreate, " IF cql_facet_find(%s_facets, '%s') != %lld THEN\n", global_proc_name, facet.ptr, (llint_t)table_crc);
bprintf(&recreate, "%s", update_tables.ptr);
bprintf(&recreate, " CALL %s_cql_set_facet_version('%s', %lld);\n", global_proc_name, facet.ptr, (llint_t)table_crc);
bprintf(&recreate, " END IF;\n");
CHARBUF_CLOSE(facet);
// once we emit, we reset the CRC we've been accumulating and reset the buffer of table recreates
table_crc = 0;
bclear(&update_tables);
}
bprintf(output, "-- recreate all the @recreate tables that might have changed\n");
bprintf(output, "@attribute(cql:private)\n");
bprintf(output, "CREATE PROCEDURE %s_cql_recreate_tables()\n", global_proc_name);
bprintf(output, "BEGIN\n");
bprintf(output, "%s", recreate.ptr);
bprintf(output, "END;\n\n");
CHARBUF_CLOSE(pending_table_creates);
CHARBUF_CLOSE(update_tables);
CHARBUF_CLOSE(recreate);
}