1 /*----------------------------------------------------------------------
2  *
3  * tableamapi.c
4  *		Support routines for API for Postgres table access methods
5  *
6  * Portions Copyright (c) 1996-2020, 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  */
33 const TableAmRoutine *
GetTableAmRoutine(Oid amhandler)34 GetTableAmRoutine(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 */
110 bool
check_default_table_access_method(char ** newval,void ** extra,GucSource source)111 check_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