2006-07-04 00:45:41 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* reloptions.c
|
|
|
|
* Core support for relation options (pg_class.reloptions)
|
|
|
|
*
|
2014-01-07 22:05:30 +01:00
|
|
|
* Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
|
2006-07-04 00:45:41 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/access/common/reloptions.c
|
2006-07-04 00:45:41 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
#include "access/gist_private.h"
|
|
|
|
#include "access/hash.h"
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2009-01-05 18:14:28 +01:00
|
|
|
#include "access/nbtree.h"
|
2006-07-04 00:45:41 +02:00
|
|
|
#include "access/reloptions.h"
|
2011-12-17 22:41:16 +01:00
|
|
|
#include "access/spgist.h"
|
2006-07-04 00:45:41 +02:00
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "commands/defrem.h"
|
2010-01-05 22:54:00 +01:00
|
|
|
#include "commands/tablespace.h"
|
2013-07-18 23:10:16 +02:00
|
|
|
#include "commands/view.h"
|
2007-12-02 00:44:44 +01:00
|
|
|
#include "nodes/makefuncs.h"
|
2011-09-04 07:13:16 +02:00
|
|
|
#include "utils/array.h"
|
2010-01-22 17:40:19 +01:00
|
|
|
#include "utils/attoptcache.h"
|
2006-07-04 00:45:41 +02:00
|
|
|
#include "utils/builtins.h"
|
2011-09-04 07:13:16 +02:00
|
|
|
#include "utils/guc.h"
|
2009-01-05 18:14:28 +01:00
|
|
|
#include "utils/memutils.h"
|
2006-07-04 00:45:41 +02:00
|
|
|
#include "utils/rel.h"
|
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
/*
|
|
|
|
* Contents of pg_class.reloptions
|
|
|
|
*
|
|
|
|
* To add an option:
|
|
|
|
*
|
2009-01-12 22:02:15 +01:00
|
|
|
* (i) decide on a type (integer, real, bool, string), name, default value,
|
|
|
|
* upper and lower bounds (if applicable); for strings, consider a validation
|
|
|
|
* routine.
|
|
|
|
* (ii) add a record below (or use add_<type>_reloption).
|
|
|
|
* (iii) add it to the appropriate options struct (perhaps StdRdOptions)
|
|
|
|
* (iv) add it to the appropriate handling routine (perhaps
|
2009-01-05 18:14:28 +01:00
|
|
|
* default_reloptions)
|
|
|
|
* (v) don't forget to document the option
|
|
|
|
*
|
|
|
|
* Note that we don't handle "oids" in relOpts because it is handled by
|
|
|
|
* interpretOidsOption().
|
|
|
|
*/
|
|
|
|
|
|
|
|
static relopt_bool boolRelOpts[] =
|
|
|
|
{
|
2009-02-09 21:57:59 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_enabled",
|
|
|
|
"Enables autovacuum in this relation",
|
2009-04-04 02:45:02 +02:00
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
|
|
|
true
|
|
|
|
},
|
2013-12-11 01:17:34 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"user_catalog_table",
|
|
|
|
"Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
|
|
|
|
RELOPT_KIND_HEAP
|
|
|
|
},
|
|
|
|
false
|
|
|
|
},
|
2009-03-24 21:17:18 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"fastupdate",
|
|
|
|
"Enables \"fast update\" feature for this GIN index",
|
|
|
|
RELOPT_KIND_GIN
|
|
|
|
},
|
|
|
|
true
|
|
|
|
},
|
2011-12-22 22:15:57 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"security_barrier",
|
|
|
|
"View acts as a row security barrier",
|
|
|
|
RELOPT_KIND_VIEW
|
|
|
|
},
|
|
|
|
false
|
|
|
|
},
|
2009-01-05 18:14:28 +01:00
|
|
|
/* list terminator */
|
2009-06-11 16:49:15 +02:00
|
|
|
{{NULL}}
|
2009-01-05 18:14:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static relopt_int intRelOpts[] =
|
|
|
|
{
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"fillfactor",
|
|
|
|
"Packs table pages only to this percentage",
|
|
|
|
RELOPT_KIND_HEAP
|
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
|
2009-01-05 18:14:28 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"fillfactor",
|
|
|
|
"Packs btree index pages only to this percentage",
|
|
|
|
RELOPT_KIND_BTREE
|
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
|
2009-01-05 18:14:28 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"fillfactor",
|
|
|
|
"Packs hash index pages only to this percentage",
|
|
|
|
RELOPT_KIND_HASH
|
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
|
2009-01-05 18:14:28 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"fillfactor",
|
|
|
|
"Packs gist index pages only to this percentage",
|
|
|
|
RELOPT_KIND_GIST
|
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
|
2009-01-05 18:14:28 +01:00
|
|
|
},
|
2011-12-17 22:41:16 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"fillfactor",
|
|
|
|
"Packs spgist index pages only to this percentage",
|
|
|
|
RELOPT_KIND_SPGIST
|
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
|
2011-12-17 22:41:16 +01:00
|
|
|
},
|
2009-02-09 21:57:59 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_vacuum_threshold",
|
|
|
|
"Minimum number of tuple updates or deletes prior to vacuum",
|
2009-04-04 02:45:02 +02:00
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
-1, 0, INT_MAX
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_analyze_threshold",
|
|
|
|
"Minimum number of tuple inserts, updates or deletes prior to analyze",
|
2010-06-07 04:59:02 +02:00
|
|
|
RELOPT_KIND_HEAP
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
-1, 0, INT_MAX
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_vacuum_cost_delay",
|
|
|
|
"Vacuum cost delay in milliseconds, for autovacuum",
|
2009-04-04 02:45:02 +02:00
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
-1, 0, 100
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_vacuum_cost_limit",
|
|
|
|
"Vacuum cost amount available before napping, for autovacuum",
|
2009-04-04 02:45:02 +02:00
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
-1, 1, 10000
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_freeze_min_age",
|
|
|
|
"Minimum age at which VACUUM should freeze a table row, for autovacuum",
|
2009-04-04 02:45:02 +02:00
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
-1, 0, 1000000000
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
Separate multixact freezing parameters from xid's
Previously we were piggybacking on transaction ID parameters to freeze
multixacts; but since there isn't necessarily any relationship between
rates of Xid and multixact consumption, this turns out not to be a good
idea.
Therefore, we now have multixact-specific freezing parameters:
vacuum_multixact_freeze_min_age: when to remove multis as we come across
them in vacuum (default to 5 million, i.e. early in comparison to Xid's
default of 50 million)
vacuum_multixact_freeze_table_age: when to force whole-table scans
instead of scanning only the pages marked as not all visible in
visibility map (default to 150 million, same as for Xids). Whichever of
both which reaches the 150 million mark earlier will cause a whole-table
scan.
autovacuum_multixact_freeze_max_age: when for cause emergency,
uninterruptible whole-table scans (default to 400 million, double as
that for Xids). This means there shouldn't be more frequent emergency
vacuuming than previously, unless multixacts are being used very
rapidly.
Backpatch to 9.3 where multixacts were made to persist enough to require
freezing. To avoid an ABI break in 9.3, VacuumStmt has a couple of
fields in an unnatural place, and StdRdOptions is split in two so that
the newly added fields can go at the end.
Patch by me, reviewed by Robert Haas, with additional input from Andres
Freund and Tom Lane.
2014-02-13 23:30:30 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_multixact_freeze_min_age",
|
|
|
|
"Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
|
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
-1, 0, 1000000000
|
Separate multixact freezing parameters from xid's
Previously we were piggybacking on transaction ID parameters to freeze
multixacts; but since there isn't necessarily any relationship between
rates of Xid and multixact consumption, this turns out not to be a good
idea.
Therefore, we now have multixact-specific freezing parameters:
vacuum_multixact_freeze_min_age: when to remove multis as we come across
them in vacuum (default to 5 million, i.e. early in comparison to Xid's
default of 50 million)
vacuum_multixact_freeze_table_age: when to force whole-table scans
instead of scanning only the pages marked as not all visible in
visibility map (default to 150 million, same as for Xids). Whichever of
both which reaches the 150 million mark earlier will cause a whole-table
scan.
autovacuum_multixact_freeze_max_age: when for cause emergency,
uninterruptible whole-table scans (default to 400 million, double as
that for Xids). This means there shouldn't be more frequent emergency
vacuuming than previously, unless multixacts are being used very
rapidly.
Backpatch to 9.3 where multixacts were made to persist enough to require
freezing. To avoid an ABI break in 9.3, VacuumStmt has a couple of
fields in an unnatural place, and StdRdOptions is split in two so that
the newly added fields can go at the end.
Patch by me, reviewed by Robert Haas, with additional input from Andres
Freund and Tom Lane.
2014-02-13 23:30:30 +01:00
|
|
|
},
|
2009-02-09 21:57:59 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_freeze_max_age",
|
|
|
|
"Age at which to autovacuum a table to prevent transaction ID wraparound",
|
2009-04-04 02:45:02 +02:00
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
-1, 100000000, 2000000000
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
Separate multixact freezing parameters from xid's
Previously we were piggybacking on transaction ID parameters to freeze
multixacts; but since there isn't necessarily any relationship between
rates of Xid and multixact consumption, this turns out not to be a good
idea.
Therefore, we now have multixact-specific freezing parameters:
vacuum_multixact_freeze_min_age: when to remove multis as we come across
them in vacuum (default to 5 million, i.e. early in comparison to Xid's
default of 50 million)
vacuum_multixact_freeze_table_age: when to force whole-table scans
instead of scanning only the pages marked as not all visible in
visibility map (default to 150 million, same as for Xids). Whichever of
both which reaches the 150 million mark earlier will cause a whole-table
scan.
autovacuum_multixact_freeze_max_age: when for cause emergency,
uninterruptible whole-table scans (default to 400 million, double as
that for Xids). This means there shouldn't be more frequent emergency
vacuuming than previously, unless multixacts are being used very
rapidly.
Backpatch to 9.3 where multixacts were made to persist enough to require
freezing. To avoid an ABI break in 9.3, VacuumStmt has a couple of
fields in an unnatural place, and StdRdOptions is split in two so that
the newly added fields can go at the end.
Patch by me, reviewed by Robert Haas, with additional input from Andres
Freund and Tom Lane.
2014-02-13 23:30:30 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_multixact_freeze_max_age",
|
|
|
|
"Multixact age at which to autovacuum a table to prevent multixact wraparound",
|
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
|
|
|
},
|
2014-08-28 22:10:47 +02:00
|
|
|
-1, 100000000, 2000000000
|
Separate multixact freezing parameters from xid's
Previously we were piggybacking on transaction ID parameters to freeze
multixacts; but since there isn't necessarily any relationship between
rates of Xid and multixact consumption, this turns out not to be a good
idea.
Therefore, we now have multixact-specific freezing parameters:
vacuum_multixact_freeze_min_age: when to remove multis as we come across
them in vacuum (default to 5 million, i.e. early in comparison to Xid's
default of 50 million)
vacuum_multixact_freeze_table_age: when to force whole-table scans
instead of scanning only the pages marked as not all visible in
visibility map (default to 150 million, same as for Xids). Whichever of
both which reaches the 150 million mark earlier will cause a whole-table
scan.
autovacuum_multixact_freeze_max_age: when for cause emergency,
uninterruptible whole-table scans (default to 400 million, double as
that for Xids). This means there shouldn't be more frequent emergency
vacuuming than previously, unless multixacts are being used very
rapidly.
Backpatch to 9.3 where multixacts were made to persist enough to require
freezing. To avoid an ABI break in 9.3, VacuumStmt has a couple of
fields in an unnatural place, and StdRdOptions is split in two so that
the newly added fields can go at the end.
Patch by me, reviewed by Robert Haas, with additional input from Andres
Freund and Tom Lane.
2014-02-13 23:30:30 +01:00
|
|
|
},
|
2009-02-09 21:57:59 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_freeze_table_age",
|
2013-12-24 02:32:29 +01:00
|
|
|
"Age at which VACUUM should perform a full table sweep to freeze row versions",
|
2009-04-04 02:45:02 +02:00
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
2014-08-28 22:10:47 +02:00
|
|
|
}, -1, 0, 2000000000
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
Separate multixact freezing parameters from xid's
Previously we were piggybacking on transaction ID parameters to freeze
multixacts; but since there isn't necessarily any relationship between
rates of Xid and multixact consumption, this turns out not to be a good
idea.
Therefore, we now have multixact-specific freezing parameters:
vacuum_multixact_freeze_min_age: when to remove multis as we come across
them in vacuum (default to 5 million, i.e. early in comparison to Xid's
default of 50 million)
vacuum_multixact_freeze_table_age: when to force whole-table scans
instead of scanning only the pages marked as not all visible in
visibility map (default to 150 million, same as for Xids). Whichever of
both which reaches the 150 million mark earlier will cause a whole-table
scan.
autovacuum_multixact_freeze_max_age: when for cause emergency,
uninterruptible whole-table scans (default to 400 million, double as
that for Xids). This means there shouldn't be more frequent emergency
vacuuming than previously, unless multixacts are being used very
rapidly.
Backpatch to 9.3 where multixacts were made to persist enough to require
freezing. To avoid an ABI break in 9.3, VacuumStmt has a couple of
fields in an unnatural place, and StdRdOptions is split in two so that
the newly added fields can go at the end.
Patch by me, reviewed by Robert Haas, with additional input from Andres
Freund and Tom Lane.
2014-02-13 23:30:30 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_multixact_freeze_table_age",
|
|
|
|
"Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
|
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
2014-08-28 22:10:47 +02:00
|
|
|
}, -1, 0, 2000000000
|
Separate multixact freezing parameters from xid's
Previously we were piggybacking on transaction ID parameters to freeze
multixacts; but since there isn't necessarily any relationship between
rates of Xid and multixact consumption, this turns out not to be a good
idea.
Therefore, we now have multixact-specific freezing parameters:
vacuum_multixact_freeze_min_age: when to remove multis as we come across
them in vacuum (default to 5 million, i.e. early in comparison to Xid's
default of 50 million)
vacuum_multixact_freeze_table_age: when to force whole-table scans
instead of scanning only the pages marked as not all visible in
visibility map (default to 150 million, same as for Xids). Whichever of
both which reaches the 150 million mark earlier will cause a whole-table
scan.
autovacuum_multixact_freeze_max_age: when for cause emergency,
uninterruptible whole-table scans (default to 400 million, double as
that for Xids). This means there shouldn't be more frequent emergency
vacuuming than previously, unless multixacts are being used very
rapidly.
Backpatch to 9.3 where multixacts were made to persist enough to require
freezing. To avoid an ABI break in 9.3, VacuumStmt has a couple of
fields in an unnatural place, and StdRdOptions is split in two so that
the newly added fields can go at the end.
Patch by me, reviewed by Robert Haas, with additional input from Andres
Freund and Tom Lane.
2014-02-13 23:30:30 +01:00
|
|
|
},
|
BRIN: Block Range Indexes
BRIN is a new index access method intended to accelerate scans of very
large tables, without the maintenance overhead of btrees or other
traditional indexes. They work by maintaining "summary" data about
block ranges. Bitmap index scans work by reading each summary tuple and
comparing them with the query quals; all pages in the range are returned
in a lossy TID bitmap if the quals are consistent with the values in the
summary tuple, otherwise not. Normal index scans are not supported
because these indexes do not store TIDs.
As new tuples are added into the index, the summary information is
updated (if the block range in which the tuple is added is already
summarized) or not; in the latter case, a subsequent pass of VACUUM or
the brin_summarize_new_values() function will create the summary
information.
For data types with natural 1-D sort orders, the summary info consists
of the maximum and the minimum values of each indexed column within each
page range. This type of operator class we call "Minmax", and we
supply a bunch of them for most data types with B-tree opclasses.
Since the BRIN code is generalized, other approaches are possible for
things such as arrays, geometric types, ranges, etc; even for things
such as enum types we could do something different than minmax with
better results. In this commit I only include minmax.
Catalog version bumped due to new builtin catalog entries.
There's more that could be done here, but this is a good step forwards.
Loosely based on ideas from Simon Riggs; code mostly by Álvaro Herrera,
with contribution by Heikki Linnakangas.
Patch reviewed by: Amit Kapila, Heikki Linnakangas, Robert Haas.
Testing help from Jeff Janes, Erik Rijkers, Emanuel Calvo.
PS:
The research leading to these results has received funding from the
European Union's Seventh Framework Programme (FP7/2007-2013) under
grant agreement n° 318633.
2014-11-07 20:38:14 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"pages_per_range",
|
|
|
|
"Number of pages that each page range covers in a BRIN index",
|
|
|
|
RELOPT_KIND_BRIN
|
|
|
|
}, 128, 1, 131072
|
|
|
|
},
|
2014-11-11 13:08:21 +01:00
|
|
|
{
|
|
|
|
{
|
2014-11-13 04:14:48 +01:00
|
|
|
"gin_pending_list_limit",
|
2014-11-11 13:08:21 +01:00
|
|
|
"Maximum size of the pending list for this GIN index, in kilobytes.",
|
|
|
|
RELOPT_KIND_GIN
|
|
|
|
},
|
|
|
|
-1, 64, MAX_KILOBYTES
|
|
|
|
},
|
Separate multixact freezing parameters from xid's
Previously we were piggybacking on transaction ID parameters to freeze
multixacts; but since there isn't necessarily any relationship between
rates of Xid and multixact consumption, this turns out not to be a good
idea.
Therefore, we now have multixact-specific freezing parameters:
vacuum_multixact_freeze_min_age: when to remove multis as we come across
them in vacuum (default to 5 million, i.e. early in comparison to Xid's
default of 50 million)
vacuum_multixact_freeze_table_age: when to force whole-table scans
instead of scanning only the pages marked as not all visible in
visibility map (default to 150 million, same as for Xids). Whichever of
both which reaches the 150 million mark earlier will cause a whole-table
scan.
autovacuum_multixact_freeze_max_age: when for cause emergency,
uninterruptible whole-table scans (default to 400 million, double as
that for Xids). This means there shouldn't be more frequent emergency
vacuuming than previously, unless multixacts are being used very
rapidly.
Backpatch to 9.3 where multixacts were made to persist enough to require
freezing. To avoid an ABI break in 9.3, VacuumStmt has a couple of
fields in an unnatural place, and StdRdOptions is split in two so that
the newly added fields can go at the end.
Patch by me, reviewed by Robert Haas, with additional input from Andres
Freund and Tom Lane.
2014-02-13 23:30:30 +01:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
/* list terminator */
|
2009-06-11 16:49:15 +02:00
|
|
|
{{NULL}}
|
2009-01-05 18:14:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static relopt_real realRelOpts[] =
|
|
|
|
{
|
2009-02-09 21:57:59 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_vacuum_scale_factor",
|
|
|
|
"Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
|
2009-04-04 02:45:02 +02:00
|
|
|
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
2009-08-27 19:18:44 +02:00
|
|
|
-1, 0.0, 100.0
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"autovacuum_analyze_scale_factor",
|
|
|
|
"Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
|
2010-06-07 04:59:02 +02:00
|
|
|
RELOPT_KIND_HEAP
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
2009-08-27 19:18:44 +02:00
|
|
|
-1, 0.0, 100.0
|
2009-02-09 21:57:59 +01:00
|
|
|
},
|
2010-01-05 22:54:00 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"seq_page_cost",
|
|
|
|
"Sets the planner's estimate of the cost of a sequentially fetched disk page.",
|
|
|
|
RELOPT_KIND_TABLESPACE
|
|
|
|
},
|
|
|
|
-1, 0.0, DBL_MAX
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"random_page_cost",
|
|
|
|
"Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
|
|
|
|
RELOPT_KIND_TABLESPACE
|
|
|
|
},
|
|
|
|
-1, 0.0, DBL_MAX
|
|
|
|
},
|
2010-01-22 17:40:19 +01:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"n_distinct",
|
|
|
|
"Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
|
|
|
|
RELOPT_KIND_ATTRIBUTE
|
|
|
|
},
|
|
|
|
0, -1.0, DBL_MAX
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"n_distinct_inherited",
|
|
|
|
"Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
|
|
|
|
RELOPT_KIND_ATTRIBUTE
|
|
|
|
},
|
|
|
|
0, -1.0, DBL_MAX
|
|
|
|
},
|
2009-01-05 18:14:28 +01:00
|
|
|
/* list terminator */
|
2009-06-11 16:49:15 +02:00
|
|
|
{{NULL}}
|
2009-01-05 18:14:28 +01:00
|
|
|
};
|
|
|
|
|
2009-03-23 17:36:27 +01:00
|
|
|
static relopt_string stringRelOpts[] =
|
2009-01-05 18:14:28 +01:00
|
|
|
{
|
2011-09-08 16:51:23 +02:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"buffering",
|
|
|
|
"Enables buffering build for this GiST index",
|
|
|
|
RELOPT_KIND_GIST
|
|
|
|
},
|
|
|
|
4,
|
|
|
|
false,
|
|
|
|
gistValidateBufferingOption,
|
|
|
|
"auto"
|
|
|
|
},
|
2013-07-18 23:10:16 +02:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"check_option",
|
|
|
|
"View has WITH CHECK OPTION defined (local or cascaded).",
|
|
|
|
RELOPT_KIND_VIEW
|
|
|
|
},
|
|
|
|
0,
|
|
|
|
true,
|
|
|
|
validateWithCheckOption,
|
|
|
|
NULL
|
|
|
|
},
|
2009-01-05 18:14:28 +01:00
|
|
|
/* list terminator */
|
2009-06-11 16:49:15 +02:00
|
|
|
{{NULL}}
|
2009-01-05 18:14:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static relopt_gen **relOpts = NULL;
|
2009-05-25 00:22:44 +02:00
|
|
|
static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
static int num_custom_options = 0;
|
2009-01-05 18:14:28 +01:00
|
|
|
static relopt_gen **custom_options = NULL;
|
2009-06-11 16:49:15 +02:00
|
|
|
static bool need_initialization = true;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
|
|
|
static void initialize_reloptions(void);
|
|
|
|
static void parse_one_reloption(relopt_value *option, char *text_str,
|
|
|
|
int text_len, bool validate);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize_reloptions
|
2009-06-11 16:49:15 +02:00
|
|
|
* initialization routine, must be called before parsing
|
2009-01-05 18:14:28 +01:00
|
|
|
*
|
|
|
|
* Initialize the relOpts array and fill each variable's type and name length.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
initialize_reloptions(void)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
int i;
|
2010-03-11 22:47:19 +01:00
|
|
|
int j;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
2010-03-11 22:47:19 +01:00
|
|
|
j = 0;
|
2009-01-05 18:14:28 +01:00
|
|
|
for (i = 0; boolRelOpts[i].gen.name; i++)
|
|
|
|
j++;
|
|
|
|
for (i = 0; intRelOpts[i].gen.name; i++)
|
|
|
|
j++;
|
|
|
|
for (i = 0; realRelOpts[i].gen.name; i++)
|
|
|
|
j++;
|
|
|
|
for (i = 0; stringRelOpts[i].gen.name; i++)
|
|
|
|
j++;
|
|
|
|
j += num_custom_options;
|
|
|
|
|
|
|
|
if (relOpts)
|
|
|
|
pfree(relOpts);
|
|
|
|
relOpts = MemoryContextAlloc(TopMemoryContext,
|
|
|
|
(j + 1) * sizeof(relopt_gen *));
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
for (i = 0; boolRelOpts[i].gen.name; i++)
|
|
|
|
{
|
|
|
|
relOpts[j] = &boolRelOpts[i].gen;
|
|
|
|
relOpts[j]->type = RELOPT_TYPE_BOOL;
|
|
|
|
relOpts[j]->namelen = strlen(relOpts[j]->name);
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; intRelOpts[i].gen.name; i++)
|
|
|
|
{
|
|
|
|
relOpts[j] = &intRelOpts[i].gen;
|
|
|
|
relOpts[j]->type = RELOPT_TYPE_INT;
|
|
|
|
relOpts[j]->namelen = strlen(relOpts[j]->name);
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; realRelOpts[i].gen.name; i++)
|
|
|
|
{
|
|
|
|
relOpts[j] = &realRelOpts[i].gen;
|
|
|
|
relOpts[j]->type = RELOPT_TYPE_REAL;
|
|
|
|
relOpts[j]->namelen = strlen(relOpts[j]->name);
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; stringRelOpts[i].gen.name; i++)
|
|
|
|
{
|
|
|
|
relOpts[j] = &stringRelOpts[i].gen;
|
|
|
|
relOpts[j]->type = RELOPT_TYPE_STRING;
|
|
|
|
relOpts[j]->namelen = strlen(relOpts[j]->name);
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < num_custom_options; i++)
|
|
|
|
{
|
|
|
|
relOpts[j] = custom_options[i];
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add a list terminator */
|
|
|
|
relOpts[j] = NULL;
|
2010-03-11 22:47:19 +01:00
|
|
|
|
|
|
|
/* flag the work is complete */
|
|
|
|
need_initialization = false;
|
2009-01-05 18:14:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add_reloption_kind
|
2009-06-11 16:49:15 +02:00
|
|
|
* Create a new relopt_kind value, to be used in custom reloptions by
|
|
|
|
* user-defined AMs.
|
2009-01-05 18:14:28 +01:00
|
|
|
*/
|
2009-04-04 02:45:02 +02:00
|
|
|
relopt_kind
|
2009-01-05 18:14:28 +01:00
|
|
|
add_reloption_kind(void)
|
|
|
|
{
|
2009-05-25 00:22:44 +02:00
|
|
|
/* don't hand out the last bit so that the enum's behavior is portable */
|
2009-01-05 18:14:28 +01:00
|
|
|
if (last_assigned_kind >= RELOPT_KIND_MAX)
|
|
|
|
ereport(ERROR,
|
2009-04-04 02:45:02 +02:00
|
|
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
2009-06-11 16:49:15 +02:00
|
|
|
errmsg("user-defined relation parameter types limit exceeded")));
|
2009-04-04 02:45:02 +02:00
|
|
|
last_assigned_kind <<= 1;
|
2009-05-25 00:22:44 +02:00
|
|
|
return (relopt_kind) last_assigned_kind;
|
2009-01-05 18:14:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add_reloption
|
2009-06-11 16:49:15 +02:00
|
|
|
* Add an already-created custom reloption to the list, and recompute the
|
|
|
|
* main parser table.
|
2009-01-05 18:14:28 +01:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_reloption(relopt_gen *newoption)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
static int max_custom_options = 0;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
|
|
|
if (num_custom_options >= max_custom_options)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
MemoryContext oldcxt;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
|
|
|
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
|
|
|
|
|
|
|
|
if (max_custom_options == 0)
|
|
|
|
{
|
|
|
|
max_custom_options = 8;
|
|
|
|
custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
max_custom_options *= 2;
|
|
|
|
custom_options = repalloc(custom_options,
|
2009-06-11 16:49:15 +02:00
|
|
|
max_custom_options * sizeof(relopt_gen *));
|
2009-01-05 18:14:28 +01:00
|
|
|
}
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
|
|
}
|
|
|
|
custom_options[num_custom_options++] = newoption;
|
|
|
|
|
|
|
|
need_initialization = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* allocate_reloption
|
2009-06-11 16:49:15 +02:00
|
|
|
* Allocate a new reloption and initialize the type-agnostic fields
|
|
|
|
* (for types other than string)
|
2009-01-05 18:14:28 +01:00
|
|
|
*/
|
|
|
|
static relopt_gen *
|
2009-04-04 02:45:02 +02:00
|
|
|
allocate_reloption(bits32 kinds, int type, char *name, char *desc)
|
2009-01-05 18:14:28 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
MemoryContext oldcxt;
|
|
|
|
size_t size;
|
|
|
|
relopt_gen *newoption;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
|
|
|
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case RELOPT_TYPE_BOOL:
|
|
|
|
size = sizeof(relopt_bool);
|
|
|
|
break;
|
|
|
|
case RELOPT_TYPE_INT:
|
|
|
|
size = sizeof(relopt_int);
|
|
|
|
break;
|
|
|
|
case RELOPT_TYPE_REAL:
|
|
|
|
size = sizeof(relopt_real);
|
|
|
|
break;
|
2011-08-09 14:25:44 +02:00
|
|
|
case RELOPT_TYPE_STRING:
|
|
|
|
size = sizeof(relopt_string);
|
|
|
|
break;
|
2009-01-05 18:14:28 +01:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unsupported option type");
|
2009-06-11 16:49:15 +02:00
|
|
|
return NULL; /* keep compiler quiet */
|
2009-01-05 18:14:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
newoption = palloc(size);
|
|
|
|
|
|
|
|
newoption->name = pstrdup(name);
|
|
|
|
if (desc)
|
|
|
|
newoption->desc = pstrdup(desc);
|
|
|
|
else
|
|
|
|
newoption->desc = NULL;
|
2009-04-04 02:45:02 +02:00
|
|
|
newoption->kinds = kinds;
|
2009-01-05 18:14:28 +01:00
|
|
|
newoption->namelen = strlen(name);
|
|
|
|
newoption->type = type;
|
|
|
|
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
|
|
|
|
|
|
return newoption;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add_bool_reloption
|
2009-06-11 16:49:15 +02:00
|
|
|
* Add a new boolean reloption
|
2009-01-05 18:14:28 +01:00
|
|
|
*/
|
|
|
|
void
|
2009-04-04 02:45:02 +02:00
|
|
|
add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)
|
2009-01-05 18:14:28 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
relopt_bool *newoption;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
2009-04-04 02:45:02 +02:00
|
|
|
newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
|
2009-01-05 18:14:28 +01:00
|
|
|
name, desc);
|
|
|
|
newoption->default_val = default_val;
|
|
|
|
|
|
|
|
add_reloption((relopt_gen *) newoption);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add_int_reloption
|
2009-06-11 16:49:15 +02:00
|
|
|
* Add a new integer reloption
|
2009-01-05 18:14:28 +01:00
|
|
|
*/
|
|
|
|
void
|
2009-04-04 02:45:02 +02:00
|
|
|
add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
|
2014-08-28 22:10:47 +02:00
|
|
|
int min_val, int max_val)
|
2009-01-05 18:14:28 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
relopt_int *newoption;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
2009-04-04 02:45:02 +02:00
|
|
|
newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
|
2009-01-05 18:14:28 +01:00
|
|
|
name, desc);
|
|
|
|
newoption->default_val = default_val;
|
|
|
|
newoption->min = min_val;
|
|
|
|
newoption->max = max_val;
|
|
|
|
|
|
|
|
add_reloption((relopt_gen *) newoption);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add_real_reloption
|
2009-06-11 16:49:15 +02:00
|
|
|
* Add a new float reloption
|
2009-01-05 18:14:28 +01:00
|
|
|
*/
|
|
|
|
void
|
2009-04-04 02:45:02 +02:00
|
|
|
add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
|
2009-06-11 16:49:15 +02:00
|
|
|
double min_val, double max_val)
|
2009-01-05 18:14:28 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
relopt_real *newoption;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
2009-04-04 02:45:02 +02:00
|
|
|
newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
|
2009-01-05 18:14:28 +01:00
|
|
|
name, desc);
|
|
|
|
newoption->default_val = default_val;
|
|
|
|
newoption->min = min_val;
|
|
|
|
newoption->max = max_val;
|
|
|
|
|
|
|
|
add_reloption((relopt_gen *) newoption);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add_string_reloption
|
|
|
|
* Add a new string reloption
|
2009-01-08 20:34:41 +01:00
|
|
|
*
|
|
|
|
* "validator" is an optional function pointer that can be used to test the
|
2014-05-06 18:12:18 +02:00
|
|
|
* validity of the values. It must elog(ERROR) when the argument string is
|
2009-01-08 20:34:41 +01:00
|
|
|
* not acceptable for the variable. Note that the default value must pass
|
|
|
|
* the validation.
|
2009-01-05 18:14:28 +01:00
|
|
|
*/
|
|
|
|
void
|
2009-04-04 02:45:02 +02:00
|
|
|
add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val,
|
2009-01-08 20:34:41 +01:00
|
|
|
validate_string_relopt validator)
|
2009-01-05 18:14:28 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
relopt_string *newoption;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
2011-08-09 14:25:44 +02:00
|
|
|
/* make sure the validator/default combination is sane */
|
|
|
|
if (validator)
|
|
|
|
(validator) (default_val);
|
2009-01-05 18:14:28 +01:00
|
|
|
|
2011-08-09 14:25:44 +02:00
|
|
|
newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
|
|
|
|
name, desc);
|
2009-01-08 20:34:41 +01:00
|
|
|
newoption->validate_cb = validator;
|
2009-01-05 18:14:28 +01:00
|
|
|
if (default_val)
|
|
|
|
{
|
2011-08-09 14:25:44 +02:00
|
|
|
newoption->default_val = MemoryContextStrdup(TopMemoryContext,
|
|
|
|
default_val);
|
|
|
|
newoption->default_len = strlen(default_val);
|
2009-01-05 18:14:28 +01:00
|
|
|
newoption->default_isnull = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-08-09 14:25:44 +02:00
|
|
|
newoption->default_val = "";
|
2009-01-05 18:14:28 +01:00
|
|
|
newoption->default_len = 0;
|
|
|
|
newoption->default_isnull = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_reloption((relopt_gen *) newoption);
|
|
|
|
}
|
2006-07-04 00:45:41 +02:00
|
|
|
|
|
|
|
/*
|
2009-04-04 23:12:31 +02:00
|
|
|
* Transform a relation options list (list of DefElem) into the text array
|
2009-02-02 20:31:40 +01:00
|
|
|
* format that is kept in pg_class.reloptions, including only those options
|
|
|
|
* that are in the passed namespace. The output values do not include the
|
|
|
|
* namespace.
|
2006-07-04 00:45:41 +02:00
|
|
|
*
|
|
|
|
* This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
|
|
|
|
* ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
|
|
|
|
* reloptions value (possibly NULL), and we replace or remove entries
|
|
|
|
* as needed.
|
|
|
|
*
|
|
|
|
* If ignoreOids is true, then we should ignore any occurrence of "oids"
|
|
|
|
* in the list (it will be or has been handled by interpretOidsOption()).
|
|
|
|
*
|
|
|
|
* Note that this is not responsible for determining whether the options
|
2009-02-02 20:31:40 +01:00
|
|
|
* are valid, but it does check that namespaces for all the options given are
|
2012-04-25 20:28:58 +02:00
|
|
|
* listed in validnsps. The NULL namespace is always valid and need not be
|
|
|
|
* explicitly listed. Passing a NULL pointer means that only the NULL
|
2009-02-02 20:31:40 +01:00
|
|
|
* namespace is valid.
|
2006-07-04 00:45:41 +02:00
|
|
|
*
|
|
|
|
* Both oldOptions and the result are text arrays (or NULL for "default"),
|
|
|
|
* but we declare them as Datums to avoid including array.h in reloptions.h.
|
|
|
|
*/
|
|
|
|
Datum
|
2009-02-02 20:31:40 +01:00
|
|
|
transformRelOptions(Datum oldOptions, List *defList, char *namspace,
|
|
|
|
char *validnsps[], bool ignoreOids, bool isReset)
|
2006-07-04 00:45:41 +02:00
|
|
|
{
|
|
|
|
Datum result;
|
|
|
|
ArrayBuildState *astate;
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
/* no change if empty list */
|
|
|
|
if (defList == NIL)
|
|
|
|
return oldOptions;
|
|
|
|
|
|
|
|
/* We build new array using accumArrayResult */
|
|
|
|
astate = NULL;
|
|
|
|
|
|
|
|
/* Copy any oldOptions that aren't to be replaced */
|
2008-04-17 23:37:28 +02:00
|
|
|
if (PointerIsValid(DatumGetPointer(oldOptions)))
|
2006-07-04 00:45:41 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
ArrayType *array = DatumGetArrayTypeP(oldOptions);
|
2006-07-04 00:45:41 +02:00
|
|
|
Datum *oldoptions;
|
|
|
|
int noldoptions;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
deconstruct_array(array, TEXTOID, -1, false, 'i',
|
|
|
|
&oldoptions, NULL, &noldoptions);
|
|
|
|
|
|
|
|
for (i = 0; i < noldoptions; i++)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
text *oldoption = DatumGetTextP(oldoptions[i]);
|
2007-02-28 00:48:10 +01:00
|
|
|
char *text_str = VARDATA(oldoption);
|
|
|
|
int text_len = VARSIZE(oldoption) - VARHDRSZ;
|
2006-07-04 00:45:41 +02:00
|
|
|
|
|
|
|
/* Search for a match in defList */
|
|
|
|
foreach(cell, defList)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
DefElem *def = (DefElem *) lfirst(cell);
|
2009-02-02 20:31:40 +01:00
|
|
|
int kw_len;
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-02-02 20:31:40 +01:00
|
|
|
/* ignore if not in the same namespace */
|
|
|
|
if (namspace == NULL)
|
|
|
|
{
|
2009-04-04 23:12:31 +02:00
|
|
|
if (def->defnamespace != NULL)
|
2009-02-02 20:31:40 +01:00
|
|
|
continue;
|
|
|
|
}
|
2009-04-04 23:12:31 +02:00
|
|
|
else if (def->defnamespace == NULL)
|
2009-02-02 20:31:40 +01:00
|
|
|
continue;
|
2009-04-04 23:12:31 +02:00
|
|
|
else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
|
2009-02-02 20:31:40 +01:00
|
|
|
continue;
|
|
|
|
|
2009-04-04 23:12:31 +02:00
|
|
|
kw_len = strlen(def->defname);
|
2006-07-04 00:45:41 +02:00
|
|
|
if (text_len > kw_len && text_str[kw_len] == '=' &&
|
2009-04-04 23:12:31 +02:00
|
|
|
pg_strncasecmp(text_str, def->defname, kw_len) == 0)
|
2006-07-04 00:45:41 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!cell)
|
|
|
|
{
|
|
|
|
/* No match, so keep old option */
|
|
|
|
astate = accumArrayResult(astate, oldoptions[i],
|
|
|
|
false, TEXTOID,
|
|
|
|
CurrentMemoryContext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* If CREATE/SET, add new options to array; if RESET, just check that the
|
|
|
|
* user didn't say RESET (option=val). (Must do this because the grammar
|
|
|
|
* doesn't enforce it.)
|
2006-07-04 00:45:41 +02:00
|
|
|
*/
|
|
|
|
foreach(cell, defList)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
DefElem *def = (DefElem *) lfirst(cell);
|
2006-07-04 00:45:41 +02:00
|
|
|
|
|
|
|
if (isReset)
|
|
|
|
{
|
|
|
|
if (def->arg != NULL)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
2006-10-04 02:30:14 +02:00
|
|
|
errmsg("RESET must not include values for parameters")));
|
2006-07-04 00:45:41 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
text *t;
|
2006-07-04 00:45:41 +02:00
|
|
|
const char *value;
|
2006-10-04 02:30:14 +02:00
|
|
|
Size len;
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-02-02 20:31:40 +01:00
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Error out if the namespace is not valid. A NULL namespace is
|
|
|
|
* always valid.
|
2009-02-02 20:31:40 +01:00
|
|
|
*/
|
2009-04-04 23:12:31 +02:00
|
|
|
if (def->defnamespace != NULL)
|
2009-02-02 20:31:40 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
bool valid = false;
|
|
|
|
int i;
|
2009-02-02 20:31:40 +01:00
|
|
|
|
|
|
|
if (validnsps)
|
|
|
|
{
|
|
|
|
for (i = 0; validnsps[i]; i++)
|
|
|
|
{
|
2009-04-04 23:12:31 +02:00
|
|
|
if (pg_strcasecmp(def->defnamespace,
|
|
|
|
validnsps[i]) == 0)
|
2009-02-02 20:31:40 +01:00
|
|
|
{
|
|
|
|
valid = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!valid)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("unrecognized parameter namespace \"%s\"",
|
2009-04-04 23:12:31 +02:00
|
|
|
def->defnamespace)));
|
2009-02-02 20:31:40 +01:00
|
|
|
}
|
|
|
|
|
2009-04-04 23:12:31 +02:00
|
|
|
if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
|
2009-02-02 20:31:40 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* ignore if not in the same namespace */
|
|
|
|
if (namspace == NULL)
|
|
|
|
{
|
2009-04-04 23:12:31 +02:00
|
|
|
if (def->defnamespace != NULL)
|
2009-02-02 20:31:40 +01:00
|
|
|
continue;
|
|
|
|
}
|
2009-04-04 23:12:31 +02:00
|
|
|
else if (def->defnamespace == NULL)
|
2009-02-02 20:31:40 +01:00
|
|
|
continue;
|
2009-04-04 23:12:31 +02:00
|
|
|
else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
|
2006-07-04 00:45:41 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
2009-04-04 23:12:31 +02:00
|
|
|
* Flatten the DefElem into a text string like "name=arg". If we
|
2009-02-02 20:31:40 +01:00
|
|
|
* have just "name", assume "name=true" is meant. Note: the
|
|
|
|
* namespace is not output.
|
2006-07-04 00:45:41 +02:00
|
|
|
*/
|
|
|
|
if (def->arg != NULL)
|
2009-04-04 23:12:31 +02:00
|
|
|
value = defGetString(def);
|
2006-07-04 00:45:41 +02:00
|
|
|
else
|
|
|
|
value = "true";
|
2009-04-04 23:12:31 +02:00
|
|
|
len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
|
2006-07-04 00:45:41 +02:00
|
|
|
/* +1 leaves room for sprintf's trailing null */
|
|
|
|
t = (text *) palloc(len + 1);
|
2007-02-28 00:48:10 +01:00
|
|
|
SET_VARSIZE(t, len);
|
2009-04-04 23:12:31 +02:00
|
|
|
sprintf(VARDATA(t), "%s=%s", def->defname, value);
|
2006-07-04 00:45:41 +02:00
|
|
|
|
|
|
|
astate = accumArrayResult(astate, PointerGetDatum(t),
|
|
|
|
false, TEXTOID,
|
|
|
|
CurrentMemoryContext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (astate)
|
|
|
|
result = makeArrayResult(astate, CurrentMemoryContext);
|
|
|
|
else
|
|
|
|
result = (Datum) 0;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-02 00:44:44 +01:00
|
|
|
/*
|
|
|
|
* Convert the text-array format of reloptions into a List of DefElem.
|
|
|
|
* This is the inverse of transformRelOptions().
|
|
|
|
*/
|
|
|
|
List *
|
|
|
|
untransformRelOptions(Datum options)
|
|
|
|
{
|
|
|
|
List *result = NIL;
|
|
|
|
ArrayType *array;
|
|
|
|
Datum *optiondatums;
|
|
|
|
int noptions;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Nothing to do if no options */
|
2008-04-17 23:37:28 +02:00
|
|
|
if (!PointerIsValid(DatumGetPointer(options)))
|
2007-12-02 00:44:44 +01:00
|
|
|
return result;
|
|
|
|
|
|
|
|
array = DatumGetArrayTypeP(options);
|
|
|
|
|
|
|
|
deconstruct_array(array, TEXTOID, -1, false, 'i',
|
|
|
|
&optiondatums, NULL, &noptions);
|
|
|
|
|
|
|
|
for (i = 0; i < noptions; i++)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
char *p;
|
|
|
|
Node *val = NULL;
|
|
|
|
|
2008-03-25 23:42:46 +01:00
|
|
|
s = TextDatumGetCString(optiondatums[i]);
|
2007-12-02 00:44:44 +01:00
|
|
|
p = strchr(s, '=');
|
|
|
|
if (p)
|
|
|
|
{
|
|
|
|
*p++ = '\0';
|
|
|
|
val = (Node *) makeString(pstrdup(p));
|
|
|
|
}
|
|
|
|
result = lappend(result, makeDefElem(pstrdup(s), val));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2009-01-26 20:41:06 +01:00
|
|
|
/*
|
|
|
|
* Extract and parse reloptions from a pg_class tuple.
|
|
|
|
*
|
|
|
|
* This is a low-level routine, expected to be used by relcache code and
|
|
|
|
* callers that do not have a table's relcache entry (e.g. autovacuum). For
|
|
|
|
* other uses, consider grabbing the rd_options pointer from the relcache entry
|
|
|
|
* instead.
|
|
|
|
*
|
|
|
|
* tupdesc is pg_class' tuple descriptor. amoptions is the amoptions regproc
|
|
|
|
* in the case of the tuple corresponding to an index, or InvalidOid otherwise.
|
|
|
|
*/
|
|
|
|
bytea *
|
|
|
|
extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
bytea *options;
|
|
|
|
bool isnull;
|
|
|
|
Datum datum;
|
|
|
|
Form_pg_class classForm;
|
2009-01-26 20:41:06 +01:00
|
|
|
|
|
|
|
datum = fastgetattr(tuple,
|
|
|
|
Anum_pg_class_reloptions,
|
|
|
|
tupdesc,
|
|
|
|
&isnull);
|
|
|
|
if (isnull)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
classForm = (Form_pg_class) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
/* Parse into appropriate format; don't error out here */
|
|
|
|
switch (classForm->relkind)
|
|
|
|
{
|
|
|
|
case RELKIND_RELATION:
|
|
|
|
case RELKIND_TOASTVALUE:
|
2013-03-04 01:23:31 +01:00
|
|
|
case RELKIND_MATVIEW:
|
2009-01-26 20:41:06 +01:00
|
|
|
options = heap_reloptions(classForm->relkind, datum, false);
|
|
|
|
break;
|
2014-07-14 23:24:40 +02:00
|
|
|
case RELKIND_VIEW:
|
|
|
|
options = view_reloptions(datum, false);
|
|
|
|
break;
|
2009-01-26 20:41:06 +01:00
|
|
|
case RELKIND_INDEX:
|
|
|
|
options = index_reloptions(amoptions, datum, false);
|
|
|
|
break;
|
2011-01-02 05:48:11 +01:00
|
|
|
case RELKIND_FOREIGN_TABLE:
|
|
|
|
options = NULL;
|
|
|
|
break;
|
2009-01-26 20:41:06 +01:00
|
|
|
default:
|
|
|
|
Assert(false); /* can't get here */
|
|
|
|
options = NULL; /* keep compiler quiet */
|
|
|
|
break;
|
|
|
|
}
|
2009-03-23 17:36:27 +01:00
|
|
|
|
2009-01-26 20:41:06 +01:00
|
|
|
return options;
|
|
|
|
}
|
2007-12-02 00:44:44 +01:00
|
|
|
|
2006-07-04 00:45:41 +02:00
|
|
|
/*
|
|
|
|
* Interpret reloptions that are given in text-array format.
|
|
|
|
*
|
2009-01-05 18:14:28 +01:00
|
|
|
* options is a reloption text array as constructed by transformRelOptions.
|
|
|
|
* kind specifies the family of options to be processed.
|
|
|
|
*
|
|
|
|
* The return value is a relopt_value * array on which the options actually
|
|
|
|
* set in the options array are marked with isset=true. The length of this
|
|
|
|
* array is returned in *numrelopts. Options not set are also present in the
|
|
|
|
* array; this is so that the caller can easily locate the default values.
|
2006-07-04 00:45:41 +02:00
|
|
|
*
|
2009-01-05 18:14:28 +01:00
|
|
|
* If there are no options of the given kind, numrelopts is set to 0 and NULL
|
|
|
|
* is returned.
|
|
|
|
*
|
|
|
|
* Note: values of type int, bool and real are allocated as part of the
|
2014-05-06 18:12:18 +02:00
|
|
|
* returned array. Values of type string are allocated separately and must
|
2009-01-05 18:14:28 +01:00
|
|
|
* be freed by the caller.
|
2006-07-04 00:45:41 +02:00
|
|
|
*/
|
2009-01-05 18:14:28 +01:00
|
|
|
relopt_value *
|
|
|
|
parseRelOptions(Datum options, bool validate, relopt_kind kind,
|
|
|
|
int *numrelopts)
|
2006-07-04 00:45:41 +02:00
|
|
|
{
|
2009-01-05 18:14:28 +01:00
|
|
|
relopt_value *reloptions;
|
|
|
|
int numoptions = 0;
|
2006-07-04 00:45:41 +02:00
|
|
|
int i;
|
2009-01-05 18:14:28 +01:00
|
|
|
int j;
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
if (need_initialization)
|
|
|
|
initialize_reloptions();
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
/* Build a list of expected options, based on kind */
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
for (i = 0; relOpts[i]; i++)
|
2009-04-04 02:45:02 +02:00
|
|
|
if (relOpts[i]->kinds & kind)
|
2009-01-05 18:14:28 +01:00
|
|
|
numoptions++;
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
if (numoptions == 0)
|
|
|
|
{
|
|
|
|
*numrelopts = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
reloptions = palloc(numoptions * sizeof(relopt_value));
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
for (i = 0, j = 0; relOpts[i]; i++)
|
|
|
|
{
|
2009-04-04 02:45:02 +02:00
|
|
|
if (relOpts[i]->kinds & kind)
|
2009-01-05 18:14:28 +01:00
|
|
|
{
|
|
|
|
reloptions[j].gen = relOpts[i];
|
|
|
|
reloptions[j].isset = false;
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Done if no options */
|
|
|
|
if (PointerIsValid(DatumGetPointer(options)))
|
2006-07-04 00:45:41 +02:00
|
|
|
{
|
2014-08-13 17:35:51 +02:00
|
|
|
ArrayType *array = DatumGetArrayTypeP(options);
|
2009-01-05 18:14:28 +01:00
|
|
|
Datum *optiondatums;
|
|
|
|
int noptions;
|
|
|
|
|
|
|
|
deconstruct_array(array, TEXTOID, -1, false, 'i',
|
|
|
|
&optiondatums, NULL, &noptions);
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
for (i = 0; i < noptions; i++)
|
2006-07-04 00:45:41 +02:00
|
|
|
{
|
2009-01-05 18:14:28 +01:00
|
|
|
text *optiontext = DatumGetTextP(optiondatums[i]);
|
|
|
|
char *text_str = VARDATA(optiontext);
|
|
|
|
int text_len = VARSIZE(optiontext) - VARHDRSZ;
|
|
|
|
int j;
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
/* Search for a match in reloptions */
|
|
|
|
for (j = 0; j < numoptions; j++)
|
2006-07-04 00:45:41 +02:00
|
|
|
{
|
2009-01-05 18:14:28 +01:00
|
|
|
int kw_len = reloptions[j].gen->namelen;
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
if (text_len > kw_len && text_str[kw_len] == '=' &&
|
|
|
|
pg_strncasecmp(text_str, reloptions[j].gen->name,
|
|
|
|
kw_len) == 0)
|
|
|
|
{
|
|
|
|
parse_one_reloption(&reloptions[j], text_str, text_len,
|
|
|
|
validate);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j >= numoptions && validate)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
s = TextDatumGetCString(optiondatums[i]);
|
|
|
|
p = strchr(s, '=');
|
|
|
|
if (p)
|
|
|
|
*p = '\0';
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("unrecognized parameter \"%s\"", s)));
|
2006-07-04 00:45:41 +02:00
|
|
|
}
|
|
|
|
}
|
2014-08-13 17:35:51 +02:00
|
|
|
|
|
|
|
/* It's worth avoiding memory leaks in this function */
|
|
|
|
pfree(optiondatums);
|
|
|
|
if (((void *) array) != DatumGetPointer(options))
|
|
|
|
pfree(array);
|
2006-07-04 00:45:41 +02:00
|
|
|
}
|
2009-01-05 18:14:28 +01:00
|
|
|
|
|
|
|
*numrelopts = numoptions;
|
|
|
|
return reloptions;
|
2006-07-04 00:45:41 +02:00
|
|
|
}
|
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
/*
|
|
|
|
* Subroutine for parseRelOptions, to parse and validate a single option's
|
|
|
|
* value
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
parse_one_reloption(relopt_value *option, char *text_str, int text_len,
|
|
|
|
bool validate)
|
|
|
|
{
|
|
|
|
char *value;
|
|
|
|
int value_len;
|
2009-01-06 04:15:51 +01:00
|
|
|
bool parsed;
|
2009-01-05 18:14:28 +01:00
|
|
|
bool nofree = false;
|
|
|
|
|
|
|
|
if (option->isset && validate)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("parameter \"%s\" specified more than once",
|
|
|
|
option->gen->name)));
|
|
|
|
|
|
|
|
value_len = text_len - option->gen->namelen - 1;
|
|
|
|
value = (char *) palloc(value_len + 1);
|
|
|
|
memcpy(value, text_str + option->gen->namelen + 1, value_len);
|
|
|
|
value[value_len] = '\0';
|
|
|
|
|
|
|
|
switch (option->gen->type)
|
|
|
|
{
|
|
|
|
case RELOPT_TYPE_BOOL:
|
|
|
|
{
|
|
|
|
parsed = parse_bool(value, &option->values.bool_val);
|
|
|
|
if (validate && !parsed)
|
|
|
|
ereport(ERROR,
|
2009-06-11 16:49:15 +02:00
|
|
|
(errmsg("invalid value for boolean option \"%s\": %s",
|
|
|
|
option->gen->name, value)));
|
2009-01-05 18:14:28 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case RELOPT_TYPE_INT:
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
relopt_int *optint = (relopt_int *) option->gen;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
2014-08-28 22:10:47 +02:00
|
|
|
parsed = parse_int(value, &option->values.int_val, 0, NULL);
|
2009-01-05 18:14:28 +01:00
|
|
|
if (validate && !parsed)
|
|
|
|
ereport(ERROR,
|
2009-06-11 16:49:15 +02:00
|
|
|
(errmsg("invalid value for integer option \"%s\": %s",
|
2014-08-28 22:10:47 +02:00
|
|
|
option->gen->name, value)));
|
2009-01-05 18:14:28 +01:00
|
|
|
if (validate && (option->values.int_val < optint->min ||
|
|
|
|
option->values.int_val > optint->max))
|
|
|
|
ereport(ERROR,
|
2009-06-11 16:49:15 +02:00
|
|
|
(errmsg("value %s out of bounds for option \"%s\"",
|
|
|
|
value, option->gen->name),
|
|
|
|
errdetail("Valid values are between \"%d\" and \"%d\".",
|
|
|
|
optint->min, optint->max)));
|
2009-01-05 18:14:28 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case RELOPT_TYPE_REAL:
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
relopt_real *optreal = (relopt_real *) option->gen;
|
2009-01-05 18:14:28 +01:00
|
|
|
|
|
|
|
parsed = parse_real(value, &option->values.real_val);
|
|
|
|
if (validate && !parsed)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errmsg("invalid value for floating point option \"%s\": %s",
|
|
|
|
option->gen->name, value)));
|
|
|
|
if (validate && (option->values.real_val < optreal->min ||
|
|
|
|
option->values.real_val > optreal->max))
|
|
|
|
ereport(ERROR,
|
2009-06-11 16:49:15 +02:00
|
|
|
(errmsg("value %s out of bounds for option \"%s\"",
|
|
|
|
value, option->gen->name),
|
|
|
|
errdetail("Valid values are between \"%f\" and \"%f\".",
|
|
|
|
optreal->min, optreal->max)));
|
2009-01-05 18:14:28 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case RELOPT_TYPE_STRING:
|
2009-01-08 20:34:41 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
relopt_string *optstring = (relopt_string *) option->gen;
|
2009-01-08 20:34:41 +01:00
|
|
|
|
|
|
|
option->values.string_val = value;
|
|
|
|
nofree = true;
|
2009-01-12 22:02:15 +01:00
|
|
|
if (validate && optstring->validate_cb)
|
|
|
|
(optstring->validate_cb) (value);
|
2009-01-08 20:34:41 +01:00
|
|
|
parsed = true;
|
|
|
|
}
|
2009-01-05 18:14:28 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unsupported reloption type %d", option->gen->type);
|
2009-06-11 16:49:15 +02:00
|
|
|
parsed = true; /* quiet compiler */
|
2009-01-05 18:14:28 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parsed)
|
|
|
|
option->isset = true;
|
|
|
|
if (!nofree)
|
|
|
|
pfree(value);
|
|
|
|
}
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-12 22:02:15 +01:00
|
|
|
/*
|
|
|
|
* Given the result from parseRelOptions, allocate a struct that's of the
|
|
|
|
* specified base size plus any extra space that's needed for string variables.
|
|
|
|
*
|
|
|
|
* "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
|
|
|
|
* equivalent).
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
allocateReloptStruct(Size base, relopt_value *options, int numoptions)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
Size size = base;
|
|
|
|
int i;
|
2009-01-12 22:02:15 +01:00
|
|
|
|
|
|
|
for (i = 0; i < numoptions; i++)
|
|
|
|
if (options[i].gen->type == RELOPT_TYPE_STRING)
|
|
|
|
size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
|
|
|
|
|
|
|
|
return palloc0(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given the result of parseRelOptions and a parsing table, fill in the
|
|
|
|
* struct (previously allocated with allocateReloptStruct) with the parsed
|
|
|
|
* values.
|
|
|
|
*
|
2009-03-23 17:36:27 +01:00
|
|
|
* rdopts is the pointer to the allocated struct to be filled.
|
|
|
|
* basesize is the sizeof(struct) that was passed to allocateReloptStruct.
|
|
|
|
* options, of length numoptions, is parseRelOptions' output.
|
|
|
|
* elems, of length numelems, is the table describing the allowed options.
|
|
|
|
* When validate is true, it is expected that all options appear in elems.
|
2009-01-12 22:02:15 +01:00
|
|
|
*/
|
|
|
|
void
|
2009-03-23 17:36:27 +01:00
|
|
|
fillRelOptions(void *rdopts, Size basesize,
|
|
|
|
relopt_value *options, int numoptions,
|
|
|
|
bool validate,
|
|
|
|
const relopt_parse_elt *elems, int numelems)
|
2009-01-12 22:02:15 +01:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
int i;
|
|
|
|
int offset = basesize;
|
2009-01-12 22:02:15 +01:00
|
|
|
|
|
|
|
for (i = 0; i < numoptions; i++)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
int j;
|
|
|
|
bool found = false;
|
2009-01-12 22:02:15 +01:00
|
|
|
|
|
|
|
for (j = 0; j < numelems; j++)
|
|
|
|
{
|
|
|
|
if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0)
|
|
|
|
{
|
|
|
|
relopt_string *optstring;
|
2009-06-11 16:49:15 +02:00
|
|
|
char *itempos = ((char *) rdopts) + elems[j].offset;
|
|
|
|
char *string_val;
|
2009-01-12 22:02:15 +01:00
|
|
|
|
|
|
|
switch (options[i].gen->type)
|
|
|
|
{
|
|
|
|
case RELOPT_TYPE_BOOL:
|
|
|
|
*(bool *) itempos = options[i].isset ?
|
|
|
|
options[i].values.bool_val :
|
|
|
|
((relopt_bool *) options[i].gen)->default_val;
|
|
|
|
break;
|
|
|
|
case RELOPT_TYPE_INT:
|
|
|
|
*(int *) itempos = options[i].isset ?
|
|
|
|
options[i].values.int_val :
|
|
|
|
((relopt_int *) options[i].gen)->default_val;
|
|
|
|
break;
|
|
|
|
case RELOPT_TYPE_REAL:
|
|
|
|
*(double *) itempos = options[i].isset ?
|
|
|
|
options[i].values.real_val :
|
|
|
|
((relopt_real *) options[i].gen)->default_val;
|
|
|
|
break;
|
|
|
|
case RELOPT_TYPE_STRING:
|
|
|
|
optstring = (relopt_string *) options[i].gen;
|
|
|
|
if (options[i].isset)
|
|
|
|
string_val = options[i].values.string_val;
|
|
|
|
else if (!optstring->default_isnull)
|
|
|
|
string_val = optstring->default_val;
|
|
|
|
else
|
|
|
|
string_val = NULL;
|
|
|
|
|
|
|
|
if (string_val == NULL)
|
|
|
|
*(int *) itempos = 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
strcpy((char *) rdopts + offset, string_val);
|
|
|
|
*(int *) itempos = offset;
|
|
|
|
offset += strlen(string_val) + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized reloption type %c",
|
|
|
|
options[i].gen->type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (validate && !found)
|
2009-03-23 17:36:27 +01:00
|
|
|
elog(ERROR, "reloption \"%s\" not found in parse table",
|
2009-01-12 22:02:15 +01:00
|
|
|
options[i].gen->name);
|
|
|
|
}
|
|
|
|
SET_VARSIZE(rdopts, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-07-04 00:45:41 +02:00
|
|
|
/*
|
2009-02-09 21:57:59 +01:00
|
|
|
* Option parser for anything that uses StdRdOptions (i.e. fillfactor and
|
|
|
|
* autovacuum)
|
2006-07-04 00:45:41 +02:00
|
|
|
*/
|
|
|
|
bytea *
|
2009-01-05 18:14:28 +01:00
|
|
|
default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
|
2006-07-04 00:45:41 +02:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
relopt_value *options;
|
|
|
|
StdRdOptions *rdopts;
|
|
|
|
int numoptions;
|
2009-03-23 17:36:27 +01:00
|
|
|
static const relopt_parse_elt tab[] = {
|
2009-02-09 21:57:59 +01:00
|
|
|
{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
|
|
|
|
{"autovacuum_enabled", RELOPT_TYPE_BOOL,
|
2009-06-11 16:49:15 +02:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, enabled)},
|
2009-02-09 21:57:59 +01:00
|
|
|
{"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
|
2009-06-11 16:49:15 +02:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_threshold)},
|
2009-02-09 21:57:59 +01:00
|
|
|
{"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
|
2009-06-11 16:49:15 +02:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_threshold)},
|
2009-02-09 21:57:59 +01:00
|
|
|
{"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
|
2009-06-11 16:49:15 +02:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_delay)},
|
2009-02-09 21:57:59 +01:00
|
|
|
{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
|
2009-06-11 16:49:15 +02:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_limit)},
|
2009-02-09 21:57:59 +01:00
|
|
|
{"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
|
2009-06-11 16:49:15 +02:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_min_age)},
|
2009-02-09 21:57:59 +01:00
|
|
|
{"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
|
2009-06-11 16:49:15 +02:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_max_age)},
|
2009-02-09 21:57:59 +01:00
|
|
|
{"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
|
2009-06-11 16:49:15 +02:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_table_age)},
|
Separate multixact freezing parameters from xid's
Previously we were piggybacking on transaction ID parameters to freeze
multixacts; but since there isn't necessarily any relationship between
rates of Xid and multixact consumption, this turns out not to be a good
idea.
Therefore, we now have multixact-specific freezing parameters:
vacuum_multixact_freeze_min_age: when to remove multis as we come across
them in vacuum (default to 5 million, i.e. early in comparison to Xid's
default of 50 million)
vacuum_multixact_freeze_table_age: when to force whole-table scans
instead of scanning only the pages marked as not all visible in
visibility map (default to 150 million, same as for Xids). Whichever of
both which reaches the 150 million mark earlier will cause a whole-table
scan.
autovacuum_multixact_freeze_max_age: when for cause emergency,
uninterruptible whole-table scans (default to 400 million, double as
that for Xids). This means there shouldn't be more frequent emergency
vacuuming than previously, unless multixacts are being used very
rapidly.
Backpatch to 9.3 where multixacts were made to persist enough to require
freezing. To avoid an ABI break in 9.3, VacuumStmt has a couple of
fields in an unnatural place, and StdRdOptions is split in two so that
the newly added fields can go at the end.
Patch by me, reviewed by Robert Haas, with additional input from Andres
Freund and Tom Lane.
2014-02-13 23:30:30 +01:00
|
|
|
{"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
|
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_min_age)},
|
|
|
|
{"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
|
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_max_age)},
|
|
|
|
{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
|
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_table_age)},
|
2009-02-09 21:57:59 +01:00
|
|
|
{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
|
2009-06-11 16:49:15 +02:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
|
2009-02-09 21:57:59 +01:00
|
|
|
{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
|
2011-12-22 22:15:57 +01:00
|
|
|
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)},
|
2013-12-11 01:17:34 +01:00
|
|
|
{"user_catalog_table", RELOPT_TYPE_BOOL,
|
2014-05-06 18:12:18 +02:00
|
|
|
offsetof(StdRdOptions, user_catalog_table)}
|
2009-01-12 22:02:15 +01:00
|
|
|
};
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
options = parseRelOptions(reloptions, validate, kind, &numoptions);
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
/* if none set, we're done */
|
|
|
|
if (numoptions == 0)
|
2006-07-04 00:45:41 +02:00
|
|
|
return NULL;
|
|
|
|
|
2009-01-12 22:02:15 +01:00
|
|
|
rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
|
2008-07-23 19:29:53 +02:00
|
|
|
|
2009-01-12 22:02:15 +01:00
|
|
|
fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
|
|
|
|
validate, tab, lengthof(tab));
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
pfree(options);
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-01-05 18:14:28 +01:00
|
|
|
return (bytea *) rdopts;
|
2006-07-04 00:45:41 +02:00
|
|
|
}
|
|
|
|
|
2014-07-14 23:24:40 +02:00
|
|
|
/*
|
|
|
|
* Option parser for views
|
|
|
|
*/
|
|
|
|
bytea *
|
|
|
|
view_reloptions(Datum reloptions, bool validate)
|
|
|
|
{
|
|
|
|
relopt_value *options;
|
|
|
|
ViewOptions *vopts;
|
|
|
|
int numoptions;
|
|
|
|
static const relopt_parse_elt tab[] = {
|
|
|
|
{"security_barrier", RELOPT_TYPE_BOOL,
|
|
|
|
offsetof(ViewOptions, security_barrier)},
|
|
|
|
{"check_option", RELOPT_TYPE_STRING,
|
|
|
|
offsetof(ViewOptions, check_option_offset)}
|
|
|
|
};
|
|
|
|
|
|
|
|
options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);
|
|
|
|
|
|
|
|
/* if none set, we're done */
|
|
|
|
if (numoptions == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
|
|
|
|
|
|
|
|
fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
|
|
|
|
validate, tab, lengthof(tab));
|
|
|
|
|
|
|
|
pfree(options);
|
|
|
|
|
|
|
|
return (bytea *) vopts;
|
|
|
|
}
|
|
|
|
|
2006-07-04 00:45:41 +02:00
|
|
|
/*
|
2011-12-22 22:15:57 +01:00
|
|
|
* Parse options for heaps, views and toast tables.
|
2006-07-04 00:45:41 +02:00
|
|
|
*/
|
|
|
|
bytea *
|
|
|
|
heap_reloptions(char relkind, Datum reloptions, bool validate)
|
|
|
|
{
|
2010-06-07 04:59:02 +02:00
|
|
|
StdRdOptions *rdopts;
|
|
|
|
|
2009-04-04 02:45:02 +02:00
|
|
|
switch (relkind)
|
|
|
|
{
|
|
|
|
case RELKIND_TOASTVALUE:
|
2010-06-07 04:59:02 +02:00
|
|
|
rdopts = (StdRdOptions *)
|
|
|
|
default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
|
|
|
|
if (rdopts != NULL)
|
|
|
|
{
|
|
|
|
/* adjust default-only parameters for TOAST relations */
|
|
|
|
rdopts->fillfactor = 100;
|
|
|
|
rdopts->autovacuum.analyze_threshold = -1;
|
|
|
|
rdopts->autovacuum.analyze_scale_factor = -1;
|
|
|
|
}
|
|
|
|
return (bytea *) rdopts;
|
2009-04-04 02:45:02 +02:00
|
|
|
case RELKIND_RELATION:
|
2013-03-04 01:23:31 +01:00
|
|
|
case RELKIND_MATVIEW:
|
2009-04-04 02:45:02 +02:00
|
|
|
return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
|
|
|
|
default:
|
2011-01-02 05:48:11 +01:00
|
|
|
/* other relkinds are not supported */
|
2009-04-04 02:45:02 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2006-07-04 00:45:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse options for indexes.
|
|
|
|
*
|
|
|
|
* amoptions Oid of option parser
|
|
|
|
* reloptions options as text[] datum
|
|
|
|
* validate error flag
|
|
|
|
*/
|
|
|
|
bytea *
|
|
|
|
index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
|
|
|
|
{
|
|
|
|
FmgrInfo flinfo;
|
|
|
|
FunctionCallInfoData fcinfo;
|
|
|
|
Datum result;
|
|
|
|
|
|
|
|
Assert(RegProcedureIsValid(amoptions));
|
|
|
|
|
|
|
|
/* Assume function is strict */
|
2008-04-17 23:37:28 +02:00
|
|
|
if (!PointerIsValid(DatumGetPointer(reloptions)))
|
2006-07-04 00:45:41 +02:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Can't use OidFunctionCallN because we might get a NULL result */
|
|
|
|
fmgr_info(amoptions, &flinfo);
|
|
|
|
|
2011-04-13 01:19:24 +02:00
|
|
|
InitFunctionCallInfoData(fcinfo, &flinfo, 2, InvalidOid, NULL, NULL);
|
2006-07-04 00:45:41 +02:00
|
|
|
|
|
|
|
fcinfo.arg[0] = reloptions;
|
|
|
|
fcinfo.arg[1] = BoolGetDatum(validate);
|
|
|
|
fcinfo.argnull[0] = false;
|
|
|
|
fcinfo.argnull[1] = false;
|
|
|
|
|
|
|
|
result = FunctionCallInvoke(&fcinfo);
|
|
|
|
|
|
|
|
if (fcinfo.isnull || DatumGetPointer(result) == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return DatumGetByteaP(result);
|
|
|
|
}
|
2010-01-05 22:54:00 +01:00
|
|
|
|
2010-01-22 17:40:19 +01:00
|
|
|
/*
|
|
|
|
* Option parser for attribute reloptions
|
|
|
|
*/
|
|
|
|
bytea *
|
|
|
|
attribute_reloptions(Datum reloptions, bool validate)
|
|
|
|
{
|
|
|
|
relopt_value *options;
|
2010-02-26 03:01:40 +01:00
|
|
|
AttributeOpts *aopts;
|
2010-01-22 17:40:19 +01:00
|
|
|
int numoptions;
|
|
|
|
static const relopt_parse_elt tab[] = {
|
|
|
|
{"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
|
|
|
|
{"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
|
|
|
|
};
|
|
|
|
|
|
|
|
options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
|
|
|
|
&numoptions);
|
|
|
|
|
|
|
|
/* if none set, we're done */
|
|
|
|
if (numoptions == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
|
|
|
|
|
|
|
|
fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
|
|
|
|
validate, tab, lengthof(tab));
|
|
|
|
|
|
|
|
pfree(options);
|
|
|
|
|
|
|
|
return (bytea *) aopts;
|
|
|
|
}
|
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
/*
|
|
|
|
* Option parser for tablespace reloptions
|
|
|
|
*/
|
|
|
|
bytea *
|
|
|
|
tablespace_reloptions(Datum reloptions, bool validate)
|
|
|
|
{
|
|
|
|
relopt_value *options;
|
2010-02-26 03:01:40 +01:00
|
|
|
TableSpaceOpts *tsopts;
|
2010-01-05 22:54:00 +01:00
|
|
|
int numoptions;
|
|
|
|
static const relopt_parse_elt tab[] = {
|
|
|
|
{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
|
|
|
|
{"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)}
|
|
|
|
};
|
|
|
|
|
|
|
|
options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
|
|
|
|
&numoptions);
|
|
|
|
|
|
|
|
/* if none set, we're done */
|
|
|
|
if (numoptions == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
|
|
|
|
|
|
|
|
fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
|
|
|
|
validate, tab, lengthof(tab));
|
|
|
|
|
|
|
|
pfree(options);
|
|
|
|
|
|
|
|
return (bytea *) tsopts;
|
|
|
|
}
|