Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rc7k8-0014Kj-6b for pgsql-hackers@arkaria.postgresql.org; Mon, 19 Feb 2024 17:47:52 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1rc7k6-00GNtS-PG for pgsql-hackers@arkaria.postgresql.org; Mon, 19 Feb 2024 17:47:50 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rbwXx-00D1KT-Tl for pgsql-hackers@lists.postgresql.org; Mon, 19 Feb 2024 05:50:34 +0000 Received: from mail-io1-xd34.google.com ([2607:f8b0:4864:20::d34]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1rbwXv-007TRo-Cg for pgsql-hackers@postgresql.org; Mon, 19 Feb 2024 05:50:32 +0000 Received: by mail-io1-xd34.google.com with SMTP id ca18e2360f4ac-7c72290deb6so123152639f.3 for ; Sun, 18 Feb 2024 21:50:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1708321830; x=1708926630; darn=postgresql.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=zNBh5uPEQ+V4cyA6DYHdZnrdTR7p6vQfQbmjN8idesk=; b=djdlbyGt3RPzahCuiuyUMMu3pjZ9oZAPT1jeSZU9KnlfoPWm8lkk7MVulz4nZmU6ZI lqz3XEel8QkcHjLAklOgYpey9s5Xa4uTjGHLFX2HTx47cD5aCrZSg0uOC5wxuzn4mqNL Iq7vqxpUlwk9gKF6fc8INa2b14J6bFqtKDQG7t9pPmioyxmdfEJSJAWccjwuI97FDzYX ss79WjG5v4rRS5sW37AF6IP/2by0ohmjfYkL4oVvwCMqCjbu43i4aHei9sqqNudAGcXz aXvPDKx7QsjqQ7uW7YmOJ/fZZcevn1OC/HgdIV9yyiPi1PfnE/HxtLLEZb6TQWl7GeVc Jq/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708321830; x=1708926630; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=zNBh5uPEQ+V4cyA6DYHdZnrdTR7p6vQfQbmjN8idesk=; b=jgbQ/7sE8Hf5Te43iqotyy8sBqKMq+GW5/1kbUrMxSxRzHVPPRCkHk7ZpRLcT459fB Hz1P0BrWro2pvlKqzBhxj1X+3ECYT1WPMR34+lHjGFUqm/Fcx1ftuSQpStC/R6xr1uar 8TtMwCrc8wwejMMPweTsUES++QAfW71PsWFwglj3bZCJHzDiVTayxBG8c+qVk1ui64qY 3wV/10tT+YtPFXipbxK6BfIGtIV8kg6AjCc/vkvdv78f3be+bS7xZ1rPPlbkRF7qmkYp h6UhJ+RpRIiD+Ta4v/4MXXblAHF/1UgkjE/ReNM4245q8q3PdalUEBoaM9tQxHQIfkcj UefA== X-Gm-Message-State: AOJu0YzXjaJSMT1YrKyDEdVjsE5ZOm4AGa3XiPSP4gdTHJZmhLEalEh6 Vc53dh8VW9KFxjzrTgmzu3886LawQ7xaujuivLFwHofWhyLvryaY0bMTzI4DWrQXVQVSqyabAbK 204GodeMjnEPD2sfuhF+aUo6E710yGB2EX3zU8g== X-Google-Smtp-Source: AGHT+IEAdXJSXtuL6jW13zhQj6vbBp2+eSKbeF0bpGH9TnXrvEB9TGQuOVP22yUIz1Z7oJzdrFryKMDnQxBzJcyKKRo= X-Received: by 2002:a92:cb02:0:b0:363:e82b:71c with SMTP id s2-20020a92cb02000000b00363e82b071cmr12870010ilo.17.1708321830380; Sun, 18 Feb 2024 21:50:30 -0800 (PST) MIME-Version: 1.0 From: Dilshod Urazov Date: Mon, 19 Feb 2024 08:50:18 +0300 Message-ID: Subject: Proposal: Adjacent B-Tree index To: pgsql-hackers@postgresql.org Content-Type: multipart/alternative; boundary="000000000000dcac2b0611b5ab7d" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000dcac2b0611b5ab7d Content-Type: text/plain; charset="UTF-8" - Motivation A regular B-tree index provides efficient mapping of key values to tuples within a table. However, if you have two tables connected in some way, a regular B-tree index may not be efficient enough. In this case, you would need to create an index for each table. The purpose will become clearer if we consider a simple example which is the main use-case as I see it. - Example We need to store a graph. So we create a table for nodes CREATE TABLE Nodes ( id SERIAL PRIMARY KEY, label VARCHAR(255) ); and a table for edges CREATE TABLE Edges ( label VARCHAR(255), source INTEGER REFERENCES Nodes(id), target INTEGER REFERENCES Nodes(id) ); In order to efficiently traverse a graph we would have an index for Nodes.id which created automatically in this case and an index for Edges.source. - Tweaked B-Tree We could optimize cases like the former by modifying PostgreSQL btree index to allow it to index 2 tables simultaneously. Semantically it would be UNIQUE index for attribute x of table A and an index for attribute y in table B. In the non-deduplicated case index tuple pointing to a tuple in A should be marked by a flag. In the deduplicated case first TID in an array can be interpreted as a link to A. During the vacuum of A an index tuple pointing to a dead tuple in A should be cleaned as well as all index tuples for the same key. We can reach this by clearing all index tuples after the dead one until we come to index tuple marked by a flag with different key. Or we can enforce deduplication in such index. In the example with a graph such index would provide PRIMARY KEY for Nodes and the index for Edges.Source. The query SELECT * FROM Nodes WHERE id = X; will use this index and take into account only a TID in Nodes (so this would be marked index tuple or first TID in a posting list). The query SELECT * FROM Edges WHERE source = X; conversely will ignore links to Nodes. -- Syntax I believe that CREATE TABLE Nodes ( id SERIAL PRIMARY KEY ADJACENT, label VARCHAR(255) ); CREATE TABLE Edges ( label VARCHAR(255), source INTEGER REFERENCES ADJACENT Nodes(id), target INTEGER REFERENCES Nodes(id) ); would suffice for this new semantics. -- Dilshod Urazov --000000000000dcac2b0611b5ab7d Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
- Motivation

A regular B-tree index provides e= fficient mapping of key values to tuples within a table. However, if you ha= ve two tables connected in some way, a regular B-tree index may not be effi= cient enough. In this case, you would need to create an index for each tabl= e. The=C2=A0purpose=C2=A0will=C2=A0become=C2=A0clearer=C2=A0if we consider= =C2=A0a simple=C2=A0example which is the main use-case as I see it.

- Example

We need to store a graph. So we create a tab= le for nodes

CREATE TABLE Nodes (
=C2=A0 id SERIAL PRIMARY= KEY,
=C2=A0 label VARCHAR(255)
);

and a table for edge= s

CREATE TABLE Edges ( =C2=A0
=C2=A0 label VARCHAR(255),=C2=A0 source INTEGER REFERENCES Nodes(id),
=C2=A0 target INTEGER REFE= RENCES Nodes(id)
);

In order to efficiently traverse a gra= ph we would have an index for Nodes.id which created automatically in this = case and an index for Edges.source.

- Tweaked B-Tree

We could optimize cases like the former by modifying PostgreSQL btree= index to allow it to index 2 tables simultaneously.

Semantic= ally it would be UNIQUE index for attribute x of table A and an index for a= ttribute y in table B. In the non-deduplicated case =C2=A0index tuple point= ing to a tuple in A should be marked by a flag. In the deduplicated case fi= rst TID in an array can be interpreted as a link to A.
During the vacuum= of A an index tuple pointing to a dead tuple in A should be cleaned as wel= l as all index tuples for the same key. We can reach this by clearing all i= ndex tuples after the dead one until we come to index tuple marked by a fla= g with different key. Or we can enforce deduplication in such index.
<= div>
In the example with a graph such index would provide PRIMARY KEY fo= r Nodes and the index for Edges.Source. The query

SELECT * FR= OM Nodes WHERE id =3D X;

will use this index and take into ac= count only a TID in Nodes (so this would be marked index tuple or first TID= in a posting list).=C2=A0 The query

SELECT * FROM Edges WHER= E source =3D X;

conversely will ignore links to Nodes.
<= div>
-- Syntax

I believe that
CREATE TABLE Nodes (
= =C2=A0 id SERIAL PRIMARY KEY ADJACENT,
=C2=A0 label VARCHAR(255)
);CREATE TABLE Edges ( =C2=A0
=C2=A0 label VARCHAR(255),
=C2=A0 sourc= e INTEGER REFERENCES ADJACENT Nodes(id),
=C2=A0 target INTEGER REFERENCE= S Nodes(id)
);

would suffice for this new semantics.
--
Dilshod Urazov
--000000000000dcac2b0611b5ab7d--