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