/src/postgres/src/backend/access/table/tableamapi.c
Line | Count | Source |
1 | | /*---------------------------------------------------------------------- |
2 | | * |
3 | | * tableamapi.c |
4 | | * Support routines for API for Postgres table access methods |
5 | | * |
6 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * src/backend/access/table/tableamapi.c |
10 | | *---------------------------------------------------------------------- |
11 | | */ |
12 | | #include "postgres.h" |
13 | | |
14 | | #include "access/tableam.h" |
15 | | #include "access/xact.h" |
16 | | #include "commands/defrem.h" |
17 | | #include "miscadmin.h" |
18 | | #include "utils/guc_hooks.h" |
19 | | |
20 | | |
21 | | /* |
22 | | * GetTableAmRoutine |
23 | | * Call the specified access method handler routine to get its |
24 | | * TableAmRoutine struct, which we expect to be statically allocated. |
25 | | */ |
26 | | const TableAmRoutine * |
27 | | GetTableAmRoutine(Oid amhandler) |
28 | 0 | { |
29 | 0 | Datum datum; |
30 | 0 | const TableAmRoutine *routine; |
31 | |
|
32 | 0 | datum = OidFunctionCall0(amhandler); |
33 | 0 | routine = (const TableAmRoutine *) DatumGetPointer(datum); |
34 | |
|
35 | 0 | if (routine == NULL || !IsA(routine, TableAmRoutine)) |
36 | 0 | elog(ERROR, "table access method handler %u did not return a TableAmRoutine struct", |
37 | 0 | amhandler); |
38 | | |
39 | | /* |
40 | | * Assert that all required callbacks are present. That makes it a bit |
41 | | * easier to keep AMs up to date, e.g. when forward porting them to a new |
42 | | * major version. |
43 | | */ |
44 | 0 | Assert(routine->scan_begin != NULL); |
45 | 0 | Assert(routine->scan_end != NULL); |
46 | 0 | Assert(routine->scan_rescan != NULL); |
47 | 0 | Assert(routine->scan_getnextslot != NULL); |
48 | |
|
49 | 0 | Assert(routine->parallelscan_estimate != NULL); |
50 | 0 | Assert(routine->parallelscan_initialize != NULL); |
51 | 0 | Assert(routine->parallelscan_reinitialize != NULL); |
52 | |
|
53 | 0 | Assert(routine->index_fetch_begin != NULL); |
54 | 0 | Assert(routine->index_fetch_reset != NULL); |
55 | 0 | Assert(routine->index_fetch_end != NULL); |
56 | 0 | Assert(routine->index_fetch_tuple != NULL); |
57 | |
|
58 | 0 | Assert(routine->tuple_fetch_row_version != NULL); |
59 | 0 | Assert(routine->tuple_tid_valid != NULL); |
60 | 0 | Assert(routine->tuple_get_latest_tid != NULL); |
61 | 0 | Assert(routine->tuple_satisfies_snapshot != NULL); |
62 | 0 | Assert(routine->index_delete_tuples != NULL); |
63 | |
|
64 | 0 | Assert(routine->tuple_insert != NULL); |
65 | | |
66 | | /* |
67 | | * Could be made optional, but would require throwing error during |
68 | | * parse-analysis. |
69 | | */ |
70 | 0 | Assert(routine->tuple_insert_speculative != NULL); |
71 | 0 | Assert(routine->tuple_complete_speculative != NULL); |
72 | |
|
73 | 0 | Assert(routine->multi_insert != NULL); |
74 | 0 | Assert(routine->tuple_delete != NULL); |
75 | 0 | Assert(routine->tuple_update != NULL); |
76 | 0 | Assert(routine->tuple_lock != NULL); |
77 | |
|
78 | 0 | Assert(routine->relation_set_new_filelocator != NULL); |
79 | 0 | Assert(routine->relation_nontransactional_truncate != NULL); |
80 | 0 | Assert(routine->relation_copy_data != NULL); |
81 | 0 | Assert(routine->relation_copy_for_cluster != NULL); |
82 | 0 | Assert(routine->relation_vacuum != NULL); |
83 | 0 | Assert(routine->scan_analyze_next_block != NULL); |
84 | 0 | Assert(routine->scan_analyze_next_tuple != NULL); |
85 | 0 | Assert(routine->index_build_range_scan != NULL); |
86 | 0 | Assert(routine->index_validate_scan != NULL); |
87 | |
|
88 | 0 | Assert(routine->relation_size != NULL); |
89 | 0 | Assert(routine->relation_needs_toast_table != NULL); |
90 | |
|
91 | 0 | Assert(routine->relation_estimate_size != NULL); |
92 | |
|
93 | 0 | Assert(routine->scan_sample_next_block != NULL); |
94 | 0 | Assert(routine->scan_sample_next_tuple != NULL); |
95 | |
|
96 | 0 | return routine; |
97 | 0 | } |
98 | | |
99 | | /* check_hook: validate new default_table_access_method */ |
100 | | bool |
101 | | check_default_table_access_method(char **newval, void **extra, GucSource source) |
102 | 2 | { |
103 | 2 | if (**newval == '\0') |
104 | 0 | { |
105 | 0 | GUC_check_errdetail("\"%s\" cannot be empty.", |
106 | 0 | "default_table_access_method"); |
107 | 0 | return false; |
108 | 0 | } |
109 | | |
110 | 2 | if (strlen(*newval) >= NAMEDATALEN) |
111 | 0 | { |
112 | 0 | GUC_check_errdetail("\"%s\" is too long (maximum %d characters).", |
113 | 0 | "default_table_access_method", NAMEDATALEN - 1); |
114 | 0 | return false; |
115 | 0 | } |
116 | | |
117 | | /* |
118 | | * If we aren't inside a transaction, or not connected to a database, we |
119 | | * cannot do the catalog access necessary to verify the method. Must |
120 | | * accept the value on faith. |
121 | | */ |
122 | 2 | if (IsTransactionState() && MyDatabaseId != InvalidOid) |
123 | 0 | { |
124 | 0 | if (!OidIsValid(get_table_am_oid(*newval, true))) |
125 | 0 | { |
126 | | /* |
127 | | * When source == PGC_S_TEST, don't throw a hard error for a |
128 | | * nonexistent table access method, only a NOTICE. See comments in |
129 | | * guc.h. |
130 | | */ |
131 | 0 | if (source == PGC_S_TEST) |
132 | 0 | { |
133 | 0 | ereport(NOTICE, |
134 | 0 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
135 | 0 | errmsg("table access method \"%s\" does not exist", |
136 | 0 | *newval))); |
137 | 0 | } |
138 | 0 | else |
139 | 0 | { |
140 | 0 | GUC_check_errdetail("Table access method \"%s\" does not exist.", |
141 | 0 | *newval); |
142 | 0 | return false; |
143 | 0 | } |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | 2 | return true; |
148 | 2 | } |