| 485 | } |
| 486 | |
| 487 | void convert_schema(TupleDesc tupdesc) |
| 488 | { |
| 489 | for (auto i = 0; i < tupdesc->natts; ++i) { |
| 490 | Form_pg_attribute attr = TupleDescAttr(tupdesc, i); |
| 491 | // Skip dropped columns |
| 492 | if (attr->attisdropped) { |
| 493 | continue; |
| 494 | } |
| 495 | if (attr->atttypid == NUMERICOID && pg::treat_numeric_as_double) { |
| 496 | constexpr int NUMERIC_PRECISION_SHIFT = 16; // Bit shift for precision in typmod |
| 497 | constexpr int NUMERIC_COMPONENT_MASK = 0xffff; // Mask for precision/scale extraction |
| 498 | |
| 499 | auto typmod = attr->atttypmod; |
| 500 | auto precision = -1; |
| 501 | auto scale = -1; |
| 502 | if (typmod >= VARHDRSZ) { |
| 503 | precision = ((typmod - VARHDRSZ) >> NUMERIC_PRECISION_SHIFT) & NUMERIC_COMPONENT_MASK; |
| 504 | scale = (typmod - VARHDRSZ) & NUMERIC_COMPONENT_MASK; |
| 505 | } |
| 506 | // Convert to FLOAT8 |
| 507 | attr->atttypid = FLOAT8OID; |
| 508 | attr->attlen = sizeof(float8); // 8 bytes |
| 509 | attr->attbyval = true; // pass by value |
| 510 | attr->attalign = TYPALIGN_DOUBLE; // 'd' - double alignment |
| 511 | attr->atttypmod = -1; // no type modifier for FLOAT8 |
| 512 | attr->attndims = 0; // not an array |
| 513 | const char* column_name = NameStr(attr->attname); |
| 514 | if (precision >= 0) { |
| 515 | elog(WARNING, |
| 516 | "Column '%s' converted from NUMERIC(%d,%d) to FLOAT8 - precision may be lost", |
| 517 | column_name, |
| 518 | precision, |
| 519 | scale); |
| 520 | } else { |
| 521 | elog(WARNING, "Column '%s' converted from NUMERIC to FLOAT8 - precision may be lost", column_name); |
| 522 | } |
| 523 | } else if (attr->atttypid == CHAROID || attr->atttypid == BPCHAROID) { |
| 524 | // Convert CHAR and BPCHAR to VARCHAR |
| 525 | attr->atttypid = VARCHAROID; |
| 526 | attr->attlen = -1; // variable length |
| 527 | attr->attbyval = false; // pass by reference |
| 528 | attr->attalign = TYPALIGN_INT; // 'i' - int4 alignment |
| 529 | attr->attstorage = TYPSTORAGE_EXTENDED; |
| 530 | const char* column_name = NameStr(attr->attname); |
| 531 | elog(WARNING, |
| 532 | "Column '%s' converted from CHAR/BPCHAR to VARCHAR as no fixed length string is supported", |
| 533 | column_name); |
| 534 | } |
| 535 | Oid base_typeid = pg::utils::get_base_type(attr->atttypid); |
| 536 | // For domain types over arrays, adjust attndims since PostgreSQL doesn't preserve it |
| 537 | const bool is_domain_over_array = (base_typeid != attr->atttypid && type_is_array(base_typeid)); |
| 538 | if (is_domain_over_array && attr->attndims == 0) { |
| 539 | // Extract dimensionality from domain's CHECK constraint |
| 540 | // Query pg_constraint and use pg_get_constraintdef() to get the constraint text |
| 541 | auto query_str = |
| 542 | fmt::format("SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE contypid = {} AND contype = 'c'", |
| 543 | attr->atttypid); |
| 544 | pg::utils::spi_connector connector; |
no test coverage detected