| 99 | } |
| 100 | |
| 101 | void convert_pg_to_nd(const pg::table_data& table_data, |
| 102 | const std::vector<Datum>& values, |
| 103 | const std::vector<uint8_t>& nulls, |
| 104 | int32_t t_len, |
| 105 | icm::string_map<nd::array>& row) |
| 106 | { |
| 107 | TupleDesc tupdesc = table_data.get_tuple_descriptor(); |
| 108 | for (auto i = 0; i < table_data.num_columns(); ++i) { |
| 109 | // Get the actual TupleDesc index for this logical column (handles dropped columns) |
| 110 | const auto tupdesc_idx = table_data.get_tupdesc_index(i); |
| 111 | Form_pg_attribute attr = TupleDescAttr(tupdesc, tupdesc_idx); |
| 112 | if (attr == nullptr) { |
| 113 | ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Invalid attribute at position %d", i))); |
| 114 | } |
| 115 | |
| 116 | // Skip SERIAL columns as they are auto-generated |
| 117 | if (attr->attidentity == 'a' || attr->attgenerated == ATTRIBUTE_GENERATED_STORED) { |
| 118 | continue; |
| 119 | } |
| 120 | |
| 121 | const auto column_name = table_data.get_atttypename(i); |
| 122 | // Skip if column is not in the input tuple (use tupdesc_idx for values/nulls arrays) |
| 123 | if (tupdesc_idx >= t_len || nulls[tupdesc_idx] == 1) { |
| 124 | // For numeric/scalar columns with NULL value, assign default (0) value |
| 125 | row[column_name] = ::get_default_value_for_null(table_data.get_base_atttypid(i)); |
| 126 | continue; |
| 127 | } |
| 128 | row[column_name] = |
| 129 | pg::utils::datum_to_nd(values[tupdesc_idx], table_data.get_base_atttypid(i), table_data.get_atttypmod(i)); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | } // unnamed namespace |
| 134 |
no test coverage detected