| Line | Hits | Source | Commit |
| 1345 |
- |
expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) |
- |
| 1346 |
- |
{ |
- |
| 1347 |
- |
List *result = NIL; |
- |
| 1348 |
- |
List *atsubcmds = NIL; |
- |
| 1349 |
- |
AttrNumber parent_attno; |
- |
| 1350 |
- |
Relation relation; |
- |
| 1351 |
- |
Relation childrel; |
- |
| 1352 |
- |
TupleDesc tupleDesc; |
- |
| 1353 |
- |
TupleConstr *constr; |
- |
| 1354 |
- |
AttrMap *attmap; |
- |
| 1355 |
- |
char *comment; |
- |
| 1356 |
- |
|
- |
| 1357 |
- |
/* |
- |
| 1358 |
- |
* Open the relation referenced by the LIKE clause. We should still have |
- |
| 1359 |
- |
* the table lock obtained by transformTableLikeClause (and this'll throw |
- |
| 1360 |
- |
* an assertion failure if not). Hence, no need to recheck privileges |
- |
| 1361 |
- |
* etc. We must open the rel by OID not name, to be sure we get the same |
- |
| 1362 |
- |
* table. |
- |
| 1363 |
- |
*/ |
- |
| 1364 |
- |
if (!OidIsValid(table_like_clause->relationOid)) |
- |
| 1365 |
- |
elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause"); |
- |
| 1366 |
- |
|
- |
| 1367 |
- |
relation = relation_open(table_like_clause->relationOid, NoLock); |
- |
| 1368 |
- |
|
- |
| 1369 |
- |
tupleDesc = RelationGetDescr(relation); |
- |
| 1370 |
- |
constr = tupleDesc->constr; |
- |
| 1371 |
- |
|
- |
| 1372 |
- |
/* |
- |
| 1373 |
- |
* Open the newly-created child relation; we have lock on that too. |
- |
| 1374 |
- |
*/ |
- |
| 1375 |
- |
childrel = relation_openrv(heapRel, NoLock); |
- |
| 1376 |
- |
|
- |
| 1377 |
- |
/* |
- |
| 1378 |
- |
* Construct a map from the LIKE relation's attnos to the child rel's. |
- |
| 1379 |
- |
* This re-checks type match etc, although it shouldn't be possible to |
- |
| 1380 |
- |
* have a failure since both tables are locked. |
- |
| 1381 |
- |
*/ |
- |
| 1382 |
- |
attmap = build_attrmap_by_name(RelationGetDescr(childrel), |
- |
| 1383 |
- |
tupleDesc, |
- |
| 1384 |
- |
false); |
- |
| 1385 |
- |
|
- |
| 1386 |
- |
/* |
- |
| 1387 |
- |
* Process defaults, if required. |
- |
| 1388 |
- |
*/ |
- |
| 1389 |
- |
if ((table_like_clause->options & |
- |
| 1390 |
- |
(CREATE_TABLE_LIKE_DEFAULTS | CREATE_TABLE_LIKE_GENERATED)) && |
- |
| 1391 |
- |
constr != NULL) |
- |
| 1392 |
- |
{ |
- |
| 1393 |
- |
for (parent_attno = 1; parent_attno <= tupleDesc->natts; |
- |
| 1394 |
- |
parent_attno++) |
- |
| 1395 |
- |
{ |
- |
| 1396 |
- |
Form_pg_attribute attribute = TupleDescAttr(tupleDesc, |
- |
| 1397 |
- |
parent_attno - 1); |
- |
| 1398 |
- |
|
- |
| 1399 |
- |
/* |
- |
| 1400 |
- |
* Ignore dropped columns in the parent. |
- |
| 1401 |
- |
*/ |
- |
| 1402 |
- |
if (attribute->attisdropped) |
- |
| 1403 |
- |
continue; |
- |
| 1404 |
- |
|
- |
| 1405 |
- |
/* |
- |
| 1406 |
- |
* Copy default, if present and it should be copied. We have |
- |
| 1407 |
- |
* separate options for plain default expressions and GENERATED |
- |
| 1408 |
- |
* defaults. |
- |
| 1409 |
- |
*/ |
- |
| 1410 |
- |
if (attribute->atthasdef && |
- |
| 1411 |
- |
(attribute->attgenerated ? |
- |
| 1412 |
- |
(table_like_clause->options & CREATE_TABLE_LIKE_GENERATED) : |
- |
| 1413 |
- |
(table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS))) |
- |
| 1414 |
- |
{ |
- |
| 1415 |
- |
Node *this_default; |
- |
| 1416 |
- |
AlterTableCmd *atsubcmd; |
- |
| 1417 |
- |
bool found_whole_row; |
- |
| 1418 |
- |
|
- |
| 1419 |
- |
this_default = TupleDescGetDefault(tupleDesc, parent_attno); |
- |
| 1420 |
- |
if (this_default == NULL) |
- |
| 1421 |
- |
elog(ERROR, "default expression not found for attribute %d of relation \"%s\"", |
- |
| 1422 |
- |
parent_attno, RelationGetRelationName(relation)); |
- |
| 1423 |
- |
|
- |
| 1424 |
- |
atsubcmd = makeNode(AlterTableCmd); |
- |
| 1425 |
- |
atsubcmd->subtype = AT_CookedColumnDefault; |
- |
| 1426 |
- |
atsubcmd->num = attmap->attnums[parent_attno - 1]; |
- |
| 1427 |
- |
atsubcmd->def = map_variable_attnos(this_default, |
- |
| 1428 |
- |
1, 0, |
- |
| 1429 |
- |
attmap, |
- |
| 1430 |
- |
InvalidOid, |
- |
| 1431 |
- |
&found_whole_row); |
- |
| 1432 |
- |
|
- |
| 1433 |
- |
/* |
- |
| 1434 |
- |
* Prevent this for the same reason as for constraints below. |
- |
| 1435 |
- |
* Note that defaults cannot contain any vars, so it's OK that |
- |
| 1436 |
- |
* the error message refers to generated columns. |
- |
| 1437 |
- |
*/ |
- |
| 1438 |
- |
if (found_whole_row) |
- |
| 1439 |
- |
ereport(ERROR, |
- |
| 1440 |
- |
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
- |
| 1441 |
- |
errmsg("cannot convert whole-row table reference"), |
- |
| 1442 |
- |
errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".", |
- |
| 1443 |
- |
NameStr(attribute->attname), |
- |
| 1444 |
- |
RelationGetRelationName(relation)))); |
- |
| 1445 |
- |
|
- |
| 1446 |
- |
atsubcmds = lappend(atsubcmds, atsubcmd); |
- |
| 1447 |
- |
} |
- |
| 1448 |
- |
} |
- |
| 1449 |
- |
} |
- |
| 1450 |
- |
|
- |
| 1451 |
- |
/* |
- |
| 1452 |
- |
* Copy CHECK constraints if requested, being careful to adjust attribute |
- |
| 1453 |
- |
* numbers so they match the child. |
- |
| 1454 |
- |
*/ |
- |
| 1455 |
- |
if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) && |
- |
| 1456 |
- |
constr != NULL) |
- |
| 1457 |
- |
{ |
- |
| 1458 |
- |
int ccnum; |
- |
| 1459 |
- |
|
- |
| 1460 |
- |
for (ccnum = 0; ccnum < constr->num_check; ccnum++) |
- |
| 1461 |
- |
{ |
- |
| 1462 |
- |
char *ccname = constr->check[ccnum].ccname; |
- |
| 1463 |
- |
char *ccbin = constr->check[ccnum].ccbin; |
- |
| 1464 |
- |
bool ccenforced = constr->check[ccnum].ccenforced; |
- |
| 1465 |
- |
bool ccnoinherit = constr->check[ccnum].ccnoinherit; |
- |
| 1466 |
- |
Node *ccbin_node; |
- |
| 1467 |
- |
bool found_whole_row; |
- |
| 1468 |
- |
Constraint *n; |
- |
| 1469 |
- |
AlterTableCmd *atsubcmd; |
- |
| 1470 |
- |
|
- |
| 1471 |
- |
ccbin_node = map_variable_attnos(stringToNode(ccbin), |
- |
| 1472 |
- |
1, 0, |
- |
| 1473 |
- |
attmap, |
- |
| 1474 |
- |
InvalidOid, &found_whole_row); |
- |
| 1475 |
- |
|
- |
| 1476 |
- |
/* |
- |
| 1477 |
- |
* We reject whole-row variables because the whole point of LIKE |
- |
| 1478 |
- |
* is that the new table's rowtype might later diverge from the |
- |
| 1479 |
- |
* parent's. So, while translation might be possible right now, |
- |
| 1480 |
- |
* it wouldn't be possible to guarantee it would work in future. |
- |
| 1481 |
- |
*/ |
- |
| 1482 |
- |
if (found_whole_row) |
- |
| 1483 |
- |
ereport(ERROR, |
- |
| 1484 |
- |
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
- |
| 1485 |
- |
errmsg("cannot convert whole-row table reference"), |
- |
| 1486 |
- |
errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".", |
- |
| 1487 |
- |
ccname, |
- |
| 1488 |
- |
RelationGetRelationName(relation)))); |
- |
| 1489 |
- |
|
- |
| 1490 |
- |
n = makeNode(Constraint); |
- |
| 1491 |
- |
n->contype = CONSTR_CHECK; |
- |
| 1492 |
- |
n->conname = pstrdup(ccname); |
- |
| 1493 |
- |
n->location = -1; |
- |
| 1494 |
- |
n->is_enforced = ccenforced; |
- |
| 1495 |
- |
n->initially_valid = ccenforced; /* sic */ |
- |
| 1496 |
- |
n->is_no_inherit = ccnoinherit; |
- |
| 1497 |
- |
n->raw_expr = NULL; |
- |
| 1498 |
- |
n->cooked_expr = nodeToString(ccbin_node); |
- |
| 1499 |
- |
|
- |
| 1500 |
- |
/* We can skip validation, since the new table should be empty. */ |
- |
| 1501 |
- |
n->skip_validation = true; |
- |
| 1502 |
- |
|
- |
| 1503 |
- |
atsubcmd = makeNode(AlterTableCmd); |
- |
| 1504 |
- |
atsubcmd->subtype = AT_AddConstraint; |
- |
| 1505 |
- |
atsubcmd->def = (Node *) n; |
- |
| 1506 |
- |
atsubcmds = lappend(atsubcmds, atsubcmd); |
- |
| 1507 |
- |
|
- |
| 1508 |
- |
/* Copy comment on constraint */ |
- |
| 1509 |
- |
if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) && |
- |
| 1510 |
- |
(comment = GetComment(get_relation_constraint_oid(RelationGetRelid(relation), |
- |
| 1511 |
- |
n->conname, false), |
- |
| 1512 |
- |
ConstraintRelationId, |
- |
| 1513 |
- |
0)) != NULL) |
- |
| 1514 |
- |
{ |
- |
| 1515 |
- |
CommentStmt *stmt = makeNode(CommentStmt); |
- |
| 1516 |
- |
|
- |
| 1517 |
- |
stmt->objtype = OBJECT_TABCONSTRAINT; |
- |
| 1518 |
- |
stmt->object = (Node *) list_make3(makeString(heapRel->schemaname), |
- |
| 1519 |
- |
makeString(heapRel->relname), |
- |
| 1520 |
- |
makeString(n->conname)); |
- |
| 1521 |
- |
stmt->comment = comment; |
- |
| 1522 |
- |
|
- |
| 1523 |
- |
result = lappend(result, stmt); |
- |
| 1524 |
- |
} |
- |
| 1525 |
- |
} |
- |
| 1526 |
- |
} |
- |
| 1527 |
- |
|
- |
| 1528 |
- |
/* |
- |
| 1529 |
- |
* If we generated any ALTER TABLE actions above, wrap them into a single |
- |
| 1530 |
- |
* ALTER TABLE command. Stick it at the front of the result, so it runs |
- |
| 1531 |
- |
* before any CommentStmts we made above. |
- |
| 1532 |
- |
*/ |
- |
| 1533 |
- |
if (atsubcmds) |
- |
| 1534 |
- |
{ |
- |
| 1535 |
- |
AlterTableStmt *atcmd = makeNode(AlterTableStmt); |
- |
| 1536 |
- |
|
- |
| 1537 |
- |
atcmd->relation = copyObject(heapRel); |
- |
| 1538 |
- |
atcmd->cmds = atsubcmds; |
- |
| 1539 |
- |
atcmd->objtype = OBJECT_TABLE; |
- |
| 1540 |
- |
atcmd->missing_ok = false; |
- |
| 1541 |
- |
result = lcons(atcmd, result); |
- |
| 1542 |
- |
} |
- |
| 1543 |
- |
|
- |
| 1544 |
- |
/* |
- |
| 1545 |
- |
* Process indexes if required. |
- |
| 1546 |
- |
*/ |
- |
| 1547 |
- |
if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) && |
- |
| 1548 |
- |
relation->rd_rel->relhasindex && |
- |
| 1549 |
- |
childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE) |
- |
| 1550 |
- |
{ |
- |
| 1551 |
- |
List *parent_indexes; |
- |
| 1552 |
- |
ListCell *l; |
- |
| 1553 |
- |
|
- |
| 1554 |
- |
parent_indexes = RelationGetIndexList(relation); |
- |
| 1555 |
- |
|
- |
| 1556 |
- |
foreach(l, parent_indexes) |
- |
| 1557 |
- |
{ |
- |
| 1558 |
- |
Oid parent_index_oid = lfirst_oid(l); |
- |
| 1559 |
- |
Relation parent_index; |
- |
| 1560 |
- |
IndexStmt *index_stmt; |
- |
| 1561 |
- |
|
- |
| 1562 |
- |
parent_index = index_open(parent_index_oid, AccessShareLock); |
- |
| 1563 |
- |
|
- |
| 1564 |
- |
/* Build CREATE INDEX statement to recreate the parent_index */ |
- |
| 1565 |
- |
index_stmt = generateClonedIndexStmt(heapRel, |
- |
| 1566 |
- |
parent_index, |
- |
| 1567 |
- |
attmap, |
- |
| 1568 |
- |
NULL); |
- |
| 1569 |
- |
|
- |
| 1570 |
- |
/* Copy comment on index, if requested */ |
- |
| 1571 |
- |
if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) |
- |
| 1572 |
- |
{ |
- |
| 1573 |
- |
comment = GetComment(parent_index_oid, RelationRelationId, 0); |
- |
| 1574 |
- |
|
- |
| 1575 |
- |
/* |
- |
| 1576 |
- |
* We make use of IndexStmt's idxcomment option, so as not to |
- |
| 1577 |
- |
* need to know now what name the index will have. |
- |
| 1578 |
- |
*/ |
- |
| 1579 |
- |
index_stmt->idxcomment = comment; |
- |
| 1580 |
- |
} |
- |
| 1581 |
- |
|
- |
| 1582 |
- |
result = lappend(result, index_stmt); |
- |
| 1583 |
- |
|
- |
| 1584 |
- |
index_close(parent_index, AccessShareLock); |
- |
| 1585 |
- |
} |
- |
| 1586 |
- |
} |
- |
| 1587 |
- |
|
- |
| 1588 |
- |
/* |
- |
| 1589 |
- |
* Process extended statistics if required. |
- |
| 1590 |
- |
*/ |
- |
| 1591 |
- |
if (table_like_clause->options & CREATE_TABLE_LIKE_STATISTICS) |
- |
| 1592 |
- |
{ |
- |
| 1593 |
- |
List *parent_extstats; |
- |
| 1594 |
- |
ListCell *l; |
- |
| 1595 |
- |
|
- |
| 1596 |
- |
parent_extstats = RelationGetStatExtList(relation); |
- |
| 1597 |
- |
|
- |
| 1598 |
- |
foreach(l, parent_extstats) |
- |
| 1599 |
- |
{ |
- |
| 1600 |
- |
Oid parent_stat_oid = lfirst_oid(l); |
- |
| 1601 |
- |
CreateStatsStmt *stats_stmt; |
- |
| 1602 |
- |
|
- |
| 1603 |
- |
stats_stmt = generateClonedExtStatsStmt(heapRel, |
- |
| 1604 |
- |
RelationGetRelid(childrel), |
- |
| 1605 |
- |
parent_stat_oid, |
- |
| 1606 |
- |
attmap); |
- |
| 1607 |
- |
|
- |
| 1608 |
- |
/* Copy comment on statistics object, if requested */ |
- |
| 1609 |
- |
if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) |
- |
| 1610 |
- |
{ |
- |
| 1611 |
- |
comment = GetComment(parent_stat_oid, StatisticExtRelationId, 0); |
- |
| 1612 |
- |
|
- |
| 1613 |
- |
/* |
- |
| 1614 |
- |
* We make use of CreateStatsStmt's stxcomment option, so as |
- |
| 1615 |
- |
* not to need to know now what name the statistics will have. |
- |
| 1616 |
- |
*/ |
- |
| 1617 |
- |
stats_stmt->stxcomment = comment; |
- |
| 1618 |
- |
} |
- |
| 1619 |
- |
|
- |
| 1620 |
- |
result = lappend(result, stats_stmt); |
- |
| 1621 |
- |
} |
- |
| 1622 |
- |
|
- |
| 1623 |
- |
list_free(parent_extstats); |
- |
| 1624 |
- |
} |
- |
| 1625 |
- |
|
- |
| 1626 |
- |
/* Process triggers if required */ |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1627 |
216 |
if ((table_like_clause->options & CREATE_TABLE_LIKE_TRIGGERS) && |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1628 |
95 |
relation->trigdesc != NULL) |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1629 |
- |
{ |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1630 |
50 |
bool include_comments = (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS); |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1631 |
- |
|
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1632 |
180 |
for (int nt = 0; nt < relation->trigdesc->numtriggers; nt++) |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1633 |
- |
{ |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1634 |
140 |
Trigger *trig = relation->trigdesc->triggers + nt; |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1635 |
140 |
Oid trigoid = trig->tgoid; |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1636 |
- |
CreateTrigStmt *trig_stmt; |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1637 |
- |
|
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1638 |
- |
/* We do not copy internal trigger to the new table */ |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1639 |
140 |
if (trig->tgisinternal) |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1640 |
0 |
continue; |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1641 |
- |
|
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1642 |
140 |
trig_stmt = generateClonedTriggerStmt(heapRel, trigoid, |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1643 |
- |
relation, attmap); |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1644 |
- |
|
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1645 |
- |
/* Copy comment on trigger, if requested */ |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1646 |
130 |
if (include_comments) |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1647 |
- |
{ |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1648 |
75 |
comment = GetComment(trigoid, TriggerRelationId, 0); |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1649 |
- |
|
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1650 |
- |
/* We make use of CreateTrigStmt's trigcomment option */ |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1651 |
75 |
trig_stmt->trigcomment = comment; |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1652 |
- |
} |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1653 |
- |
|
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1654 |
130 |
result = lappend(result, trig_stmt); |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1655 |
- |
} |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1656 |
- |
} |
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1657 |
- |
|
e73f551CREATE TABLE LIKE INCLUDING TRIGGERS |
| 1658 |
- |
/* Done with child rel */ |
- |
| 1659 |
- |
table_close(childrel, NoLock); |
- |
| 1660 |
- |
|
- |
| 1661 |
- |
/* |
- |
| 1662 |
- |
* Close the parent rel, but keep our AccessShareLock on it until xact |
- |
| 1663 |
- |
* commit. That will prevent someone else from deleting or ALTERing the |
- |
| 1664 |
- |
* parent before the child is committed. |
- |
| 1665 |
- |
*/ |
- |
| 1666 |
- |
table_close(relation, NoLock); |
- |
| 1667 |
- |
|
- |
| 1668 |
- |
return result; |
- |
| 1669 |
- |
} |
- |