in sources/rewrite.c [1641:1782]
static void rewrite_column_calculation(ast_node *column_calculation, jfind_t *jfind) {
Contract(is_ast_column_calculation(column_calculation));
bool_t distinct = !!column_calculation->right;
symtab *used_names = distinct ? symtab_new() : NULL;
ast_node *tail = NULL;
ast_node *head = NULL;
for (ast_node *item = column_calculation->left; item; item = item->right) {
Contract(is_ast_col_calcs(item));
EXTRACT(col_calc, item->left);
if (is_ast_dot(col_calc->left)) {
// If a column is explicitly mentioned, we simply emit it
// we won't duplicate the column later but neither will we
// filter it out if distinct is mentioned, this is to prevent
// bogus manual columns from staying in select lists. If it's
// not distinct, either hoist it to the front or else remove it.
EXTRACT_NOTNULL(dot, col_calc->left);
EXTRACT_STRING(left, dot->left);
EXTRACT_STRING(right, dot->right);
// no type check is needed here, we just emit the name whatever it is
append_scoped_name(&head, &tail, left, right);
if (used_names) {
symtab_add(used_names, right, NULL);
}
}
else if (col_calc->left) {
EXTRACT_STRING(scope, col_calc->left);
sem_struct *sptr_table = jfind_table(jfind, scope);
if (!sptr_table) {
report_error(col_calc->left, "CQL0069: name not found", scope);
record_error(column_calculation);
goto cleanup;
}
EXTRACT(like, col_calc->right);
sem_struct *sptr;
if (like) {
ast_node *found_shape = sem_find_likeable_ast(like, LIKEABLE_FOR_VALUES);
if (!found_shape) {
record_error(column_calculation);
goto cleanup;
}
// get just the shape columns (or try anyway)
sptr = found_shape->sem->sptr;
}
else {
// get all the columns from this table
sptr = sptr_table;
}
for (int32_t j = 0; j < sptr->count; j++) {
CSTR col = sptr->names[j];
if (used_names && !symtab_add(used_names, col, NULL)) {
continue;
}
append_scoped_name(&head, &tail, scope, col);
if (!verify_matched_column(tail, sptr, j, sptr_table, scope)) {
record_error(column_calculation);
goto cleanup;
}
}
}
else {
// the other case has just a like expression
EXTRACT_NOTNULL(like, col_calc->right);
ast_node *found_shape = sem_find_likeable_ast(like, LIKEABLE_FOR_VALUES);
if (!found_shape) {
record_error(column_calculation);
goto cleanup;
}
// get just the shape columns (or try anyway)
sem_struct *sptr = found_shape->sem->sptr;
// now we can use our found structure from the like
// we will find the table that has the given column
// we generate a disambiguation scope if it is needed
for (int32_t i = 0; i < sptr->count; i++) {
CSTR col = sptr->names[i];
if (!used_names || symtab_add(used_names, col, NULL)) {
// if the name has duplicates then qualify it
symtab_entry *entry = symtab_find(jfind->location, col);
if (!entry) {
report_error(like, "CQL0069: name not found", col);
record_error(column_calculation);
goto cleanup;
}
CSTR scope = (CSTR)entry->val;
sem_struct *sptr_table = jfind_table(jfind, scope);
Invariant(sptr_table); // this is our lookup of a scope that is known, it cant fail
// We only use the scope in the output if it's needed and if distinct was specified
// if distinct wasn't specified then ambiguity is an error and it will be. The later
// stages will check for an unambiguous name.
CSTR used_scope = (used_names && symtab_find(jfind->dups, col)) ? scope : NULL;
append_scoped_name(&head, &tail, used_scope, col);
// We check the type of the first match of the name, this is the only column that
// can match legally. If there are other columns ambiguity errors will be emitted.
if (!verify_matched_column(tail, sptr, i, sptr_table, scope)) {
record_error(column_calculation);
goto cleanup;
}
}
}
}
}
// replace the calc node with the head payload
ast_node *splice = column_calculation->parent;
ast_set_left(splice, head->left);
ast_set_right(tail, splice->right); // this could be mutating the head
ast_set_right(splice, head->right); // works even if head is an alias for tail
record_ok(column_calculation);
cleanup:
if (used_names) {
symtab_delete(used_names);
}
}