mirror of
https://github.com/postgres/postgres.git
synced 2025-10-12 07:05:03 +03:00
Indexes with INCLUDE columns and their support in B-tree
This patch introduces INCLUDE clause to index definition. This clause specifies a list of columns which will be included as a non-key part in the index. The INCLUDE columns exist solely to allow more queries to benefit from index-only scans. Also, such columns don't need to have appropriate operator classes. Expressions are not supported as INCLUDE columns since they cannot be used in index-only scans. Index access methods supporting INCLUDE are indicated by amcaninclude flag in IndexAmRoutine. For now, only B-tree indexes support INCLUDE clause. In B-tree indexes INCLUDE columns are truncated from pivot index tuples (tuples located in non-leaf pages and high keys). Therefore, B-tree indexes now might have variable number of attributes. This patch also provides generic facility to support that: pivot tuples contain number of their attributes in t_tid.ip_posid. Free 13th bit of t_info is used for indicating that. This facility will simplify further support of index suffix truncation. The changes of above are backward-compatible, pg_upgrade doesn't need special handling of B-tree indexes for that. Bump catalog version Author: Anastasia Lubennikova with contribition by Alexander Korotkov and me Reviewed by: Peter Geoghegan, Tomas Vondra, Antonin Houska, Jeff Janes, David Rowley, Alexander Korotkov Discussion: https://www.postgresql.org/message-id/flat/56168952.4010101@postgrespro.ru
This commit is contained in:
@@ -100,7 +100,7 @@ static remoteConn *getConnectionByName(const char *name);
|
||||
static HTAB *createConnHash(void);
|
||||
static void createNewConnection(const char *name, remoteConn *rconn);
|
||||
static void deleteConnection(const char *name);
|
||||
static char **get_pkey_attnames(Relation rel, int16 *numatts);
|
||||
static char **get_pkey_attnames(Relation rel, int16 *indnkeyatts);
|
||||
static char **get_text_array_contents(ArrayType *array, int *numitems);
|
||||
static char *get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals);
|
||||
static char *get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **tgt_pkattvals);
|
||||
@@ -1493,7 +1493,7 @@ PG_FUNCTION_INFO_V1(dblink_get_pkey);
|
||||
Datum
|
||||
dblink_get_pkey(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int16 numatts;
|
||||
int16 indnkeyatts;
|
||||
char **results;
|
||||
FuncCallContext *funcctx;
|
||||
int32 call_cntr;
|
||||
@@ -1519,7 +1519,7 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
|
||||
rel = get_rel_from_relname(PG_GETARG_TEXT_PP(0), AccessShareLock, ACL_SELECT);
|
||||
|
||||
/* get the array of attnums */
|
||||
results = get_pkey_attnames(rel, &numatts);
|
||||
results = get_pkey_attnames(rel, &indnkeyatts);
|
||||
|
||||
relation_close(rel, AccessShareLock);
|
||||
|
||||
@@ -1539,9 +1539,9 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
|
||||
attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
||||
funcctx->attinmeta = attinmeta;
|
||||
|
||||
if ((results != NULL) && (numatts > 0))
|
||||
if ((results != NULL) && (indnkeyatts > 0))
|
||||
{
|
||||
funcctx->max_calls = numatts;
|
||||
funcctx->max_calls = indnkeyatts;
|
||||
|
||||
/* got results, keep track of them */
|
||||
funcctx->user_fctx = results;
|
||||
@@ -2029,10 +2029,10 @@ dblink_fdw_validator(PG_FUNCTION_ARGS)
|
||||
* get_pkey_attnames
|
||||
*
|
||||
* Get the primary key attnames for the given relation.
|
||||
* Return NULL, and set numatts = 0, if no primary key exists.
|
||||
* Return NULL, and set indnkeyatts = 0, if no primary key exists.
|
||||
*/
|
||||
static char **
|
||||
get_pkey_attnames(Relation rel, int16 *numatts)
|
||||
get_pkey_attnames(Relation rel, int16 *indnkeyatts)
|
||||
{
|
||||
Relation indexRelation;
|
||||
ScanKeyData skey;
|
||||
@@ -2042,8 +2042,8 @@ get_pkey_attnames(Relation rel, int16 *numatts)
|
||||
char **result = NULL;
|
||||
TupleDesc tupdesc;
|
||||
|
||||
/* initialize numatts to 0 in case no primary key exists */
|
||||
*numatts = 0;
|
||||
/* initialize indnkeyatts to 0 in case no primary key exists */
|
||||
*indnkeyatts = 0;
|
||||
|
||||
tupdesc = rel->rd_att;
|
||||
|
||||
@@ -2064,12 +2064,12 @@ get_pkey_attnames(Relation rel, int16 *numatts)
|
||||
/* we're only interested if it is the primary key */
|
||||
if (index->indisprimary)
|
||||
{
|
||||
*numatts = index->indnatts;
|
||||
if (*numatts > 0)
|
||||
*indnkeyatts = index->indnkeyatts;
|
||||
if (*indnkeyatts > 0)
|
||||
{
|
||||
result = (char **) palloc(*numatts * sizeof(char *));
|
||||
result = (char **) palloc(*indnkeyatts * sizeof(char *));
|
||||
|
||||
for (i = 0; i < *numatts; i++)
|
||||
for (i = 0; i < *indnkeyatts; i++)
|
||||
result[i] = SPI_fname(tupdesc, index->indkey.values[i]);
|
||||
}
|
||||
break;
|
||||
|
@@ -54,6 +54,61 @@ SELECT dblink_build_sql_delete('foo','1 2',2,'{"0", "a"}');
|
||||
-- too many pk fields, should fail
|
||||
SELECT dblink_build_sql_delete('foo','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}');
|
||||
ERROR: invalid attribute number 4
|
||||
-- repeat the test for table with primary key index with included columns
|
||||
CREATE TABLE foo_1(f1 int, f2 text, f3 text[], primary key (f1,f2) include (f3));
|
||||
INSERT INTO foo_1 VALUES (0,'a','{"a0","b0","c0"}');
|
||||
INSERT INTO foo_1 VALUES (1,'b','{"a1","b1","c1"}');
|
||||
INSERT INTO foo_1 VALUES (2,'c','{"a2","b2","c2"}');
|
||||
INSERT INTO foo_1 VALUES (3,'d','{"a3","b3","c3"}');
|
||||
INSERT INTO foo_1 VALUES (4,'e','{"a4","b4","c4"}');
|
||||
INSERT INTO foo_1 VALUES (5,'f','{"a5","b5","c5"}');
|
||||
INSERT INTO foo_1 VALUES (6,'g','{"a6","b6","c6"}');
|
||||
INSERT INTO foo_1 VALUES (7,'h','{"a7","b7","c7"}');
|
||||
INSERT INTO foo_1 VALUES (8,'i','{"a8","b8","c8"}');
|
||||
INSERT INTO foo_1 VALUES (9,'j','{"a9","b9","c9"}');
|
||||
-- misc utilities
|
||||
-- list the primary key fields
|
||||
SELECT *
|
||||
FROM dblink_get_pkey('foo_1');
|
||||
position | colname
|
||||
----------+---------
|
||||
1 | f1
|
||||
2 | f2
|
||||
(2 rows)
|
||||
|
||||
-- build an insert statement based on a local tuple,
|
||||
-- replacing the primary key values with new ones
|
||||
SELECT dblink_build_sql_insert('foo_1','1 2',2,'{"0", "a"}','{"99", "xyz"}');
|
||||
dblink_build_sql_insert
|
||||
-------------------------------------------------------------
|
||||
INSERT INTO foo_1(f1,f2,f3) VALUES('99','xyz','{a0,b0,c0}')
|
||||
(1 row)
|
||||
|
||||
-- too many pk fields, should fail
|
||||
SELECT dblink_build_sql_insert('foo_1','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}','{"99", "xyz", "{za0,zb0,zc0}"}');
|
||||
ERROR: invalid attribute number 4
|
||||
-- build an update statement based on a local tuple,
|
||||
-- replacing the primary key values with new ones
|
||||
SELECT dblink_build_sql_update('foo_1','1 2',2,'{"0", "a"}','{"99", "xyz"}');
|
||||
dblink_build_sql_update
|
||||
------------------------------------------------------------------------------------------
|
||||
UPDATE foo_1 SET f1 = '99', f2 = 'xyz', f3 = '{a0,b0,c0}' WHERE f1 = '99' AND f2 = 'xyz'
|
||||
(1 row)
|
||||
|
||||
-- too many pk fields, should fail
|
||||
SELECT dblink_build_sql_update('foo_1','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}','{"99", "xyz", "{za0,zb0,zc0}"}');
|
||||
ERROR: invalid attribute number 4
|
||||
-- build a delete statement based on a local tuple,
|
||||
SELECT dblink_build_sql_delete('foo_1','1 2',2,'{"0", "a"}');
|
||||
dblink_build_sql_delete
|
||||
-----------------------------------------------
|
||||
DELETE FROM foo_1 WHERE f1 = '0' AND f2 = 'a'
|
||||
(1 row)
|
||||
|
||||
-- too many pk fields, should fail
|
||||
SELECT dblink_build_sql_delete('foo_1','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}');
|
||||
ERROR: invalid attribute number 4
|
||||
DROP TABLE foo_1;
|
||||
-- retest using a quoted and schema qualified table
|
||||
CREATE SCHEMA "MySchema";
|
||||
CREATE TABLE "MySchema"."Foo"(f1 int, f2 text, f3 text[], primary key (f1,f2));
|
||||
|
@@ -38,6 +38,44 @@ SELECT dblink_build_sql_delete('foo','1 2',2,'{"0", "a"}');
|
||||
-- too many pk fields, should fail
|
||||
SELECT dblink_build_sql_delete('foo','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}');
|
||||
|
||||
-- repeat the test for table with primary key index with included columns
|
||||
CREATE TABLE foo_1(f1 int, f2 text, f3 text[], primary key (f1,f2) include (f3));
|
||||
INSERT INTO foo_1 VALUES (0,'a','{"a0","b0","c0"}');
|
||||
INSERT INTO foo_1 VALUES (1,'b','{"a1","b1","c1"}');
|
||||
INSERT INTO foo_1 VALUES (2,'c','{"a2","b2","c2"}');
|
||||
INSERT INTO foo_1 VALUES (3,'d','{"a3","b3","c3"}');
|
||||
INSERT INTO foo_1 VALUES (4,'e','{"a4","b4","c4"}');
|
||||
INSERT INTO foo_1 VALUES (5,'f','{"a5","b5","c5"}');
|
||||
INSERT INTO foo_1 VALUES (6,'g','{"a6","b6","c6"}');
|
||||
INSERT INTO foo_1 VALUES (7,'h','{"a7","b7","c7"}');
|
||||
INSERT INTO foo_1 VALUES (8,'i','{"a8","b8","c8"}');
|
||||
INSERT INTO foo_1 VALUES (9,'j','{"a9","b9","c9"}');
|
||||
|
||||
-- misc utilities
|
||||
|
||||
-- list the primary key fields
|
||||
SELECT *
|
||||
FROM dblink_get_pkey('foo_1');
|
||||
|
||||
-- build an insert statement based on a local tuple,
|
||||
-- replacing the primary key values with new ones
|
||||
SELECT dblink_build_sql_insert('foo_1','1 2',2,'{"0", "a"}','{"99", "xyz"}');
|
||||
-- too many pk fields, should fail
|
||||
SELECT dblink_build_sql_insert('foo_1','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}','{"99", "xyz", "{za0,zb0,zc0}"}');
|
||||
|
||||
-- build an update statement based on a local tuple,
|
||||
-- replacing the primary key values with new ones
|
||||
SELECT dblink_build_sql_update('foo_1','1 2',2,'{"0", "a"}','{"99", "xyz"}');
|
||||
-- too many pk fields, should fail
|
||||
SELECT dblink_build_sql_update('foo_1','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}','{"99", "xyz", "{za0,zb0,zc0}"}');
|
||||
|
||||
-- build a delete statement based on a local tuple,
|
||||
SELECT dblink_build_sql_delete('foo_1','1 2',2,'{"0", "a"}');
|
||||
-- too many pk fields, should fail
|
||||
SELECT dblink_build_sql_delete('foo_1','1 2 3 4',4,'{"0", "a", "{a0,b0,c0}"}');
|
||||
|
||||
DROP TABLE foo_1;
|
||||
|
||||
-- retest using a quoted and schema qualified table
|
||||
CREATE SCHEMA "MySchema";
|
||||
CREATE TABLE "MySchema"."Foo"(f1 int, f2 text, f3 text[], primary key (f1,f2));
|
||||
|
Reference in New Issue
Block a user