static void rewrite_column_calculation()

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);
  }
}