Skip to content

Commit 6729420

Browse files
Add YAML integration tests and documentation for cross-schema joins
cross-schema-join.yamsql tests basic joins, WHERE pushdown, standalone secondary-schema queries, three-schema joins, and same-named table type-collision avoidance across five schemas in a single database. YamlIntegrationTests wires in the new yamsql file. Joins.rst documents the schema_name.table_name qualifier syntax, the same-database constraint, and multi-schema join examples.
1 parent 617857d commit 6729420

3 files changed

Lines changed: 183 additions & 0 deletions

File tree

docs/sphinx/source/reference/Joins.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,40 @@ User-defined functions can be used like tables in the ``FROM`` clause and joined
348348
FROM f1(103, 'b') A, f1(103, 'b') B
349349
WHERE A.col1 = B.col1
350350
351+
Cross-Schema Joins
352+
==================
353+
354+
Tables from different schemas within the same database can be joined using the ``schema_name.table_name``
355+
qualifier on any table reference in a FROM clause or JOIN target:
356+
357+
.. code-block:: sql
358+
359+
SELECT a.name, b.tag
360+
FROM items AS a
361+
JOIN other_schema.tags AS b ON a.id = b.item_id
362+
363+
The qualifier ``other_schema`` must name a schema in the **same database** as the current connection.
364+
Cross-database joins are not supported.
365+
366+
Unqualified names always resolve to the current connection schema. The qualified form can appear
367+
on either or both sides of a join, or as a standalone table reference without any join:
368+
369+
.. code-block:: sql
370+
371+
SELECT tag FROM other_schema.tags ORDER BY item_id
372+
373+
Joins spanning more than two schemas are also supported:
374+
375+
.. code-block:: sql
376+
377+
SELECT a.name, b.tag, c.price
378+
FROM items AS a
379+
JOIN schema_b.tags AS b ON a.id = b.item_id
380+
JOIN schema_c.prices AS c ON a.id = c.item_id
381+
351382
Important notes
352383
===============
384+
===============
353385

354386
Table aliases
355387
-------------

yaml-tests/src/test/java/YamlIntegrationTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,4 +548,14 @@ public void filterIndexTest(YamlTest.Runner runner) throws Exception {
548548
public void selectWithoutFrom(YamlTest.Runner runner) throws Exception {
549549
runner.runYamsql("select-without-from.yamsql");
550550
}
551+
552+
@TestTemplate
553+
public void tableAsColumnTests(YamlTest.Runner runner) throws Exception {
554+
runner.runYamsql("table-as-column-tests.yamsql");
555+
}
556+
557+
@TestTemplate
558+
public void crossSchemaJoinTests(YamlTest.Runner runner) throws Exception {
559+
runner.runYamsql("cross-schema-join.yamsql");
560+
}
551561
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#
2+
# cross-schema-join.yamsql
3+
#
4+
# This source file is part of the FoundationDB open source project
5+
#
6+
# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors
7+
#
8+
# Licensed under the Apache License, Version 2.0 (the "License");
9+
# you may not use this file except in compliance with the License.
10+
# You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing, software
15+
# distributed under the License is distributed on an "AS IS" BASIS,
16+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
# See the License for the specific language governing permissions and
18+
# limitations under the License.
19+
20+
# Integration tests for cross-schema query support.
21+
# Verifies that tables from different schemas within the same database can be
22+
# joined or queried using the schema_name.table_name syntax.
23+
---
24+
options:
25+
supported_version: !current_version
26+
---
27+
# Cleanup from any previous run
28+
setup:
29+
connect: "jdbc:embed:/__SYS?schema=CATALOG"
30+
steps:
31+
- query: drop database if exists /FRL/CROSS_SCHEMA_JOIN
32+
- query: drop schema template if exists cross_schema_items_template
33+
- query: drop schema template if exists cross_schema_tags_template
34+
- query: drop schema template if exists cross_schema_prices_template
35+
- query: drop schema template if exists cross_schema_things_v1_template
36+
- query: drop schema template if exists cross_schema_things_v2_template
37+
---
38+
# Create templates, database, and all schemas
39+
setup:
40+
connect: "jdbc:embed:/__SYS?schema=CATALOG"
41+
steps:
42+
- query: create schema template cross_schema_items_template
43+
create table items(id bigint, name string, primary key(id))
44+
- query: create schema template cross_schema_tags_template
45+
create table tags(item_id bigint, tag string, primary key(item_id))
46+
- query: create schema template cross_schema_prices_template
47+
create table prices(item_id bigint, price bigint, primary key(item_id))
48+
# Two templates with identically-named tables but different columns,
49+
# to exercise type-namespace collision avoidance.
50+
- query: create schema template cross_schema_things_v1_template
51+
create table things(id bigint, description string, primary key(id))
52+
- query: create schema template cross_schema_things_v2_template
53+
create table things(id bigint, quantity bigint, primary key(id))
54+
- query: create database /FRL/CROSS_SCHEMA_JOIN
55+
- query: create schema /FRL/CROSS_SCHEMA_JOIN/ITEMS_SCHEMA with template cross_schema_items_template
56+
- query: create schema /FRL/CROSS_SCHEMA_JOIN/TAGS_SCHEMA with template cross_schema_tags_template
57+
- query: create schema /FRL/CROSS_SCHEMA_JOIN/PRICES_SCHEMA with template cross_schema_prices_template
58+
- query: create schema /FRL/CROSS_SCHEMA_JOIN/DESCRIPTION_SCHEMA with template cross_schema_things_v1_template
59+
- query: create schema /FRL/CROSS_SCHEMA_JOIN/QUANTITY_SCHEMA with template cross_schema_things_v2_template
60+
---
61+
setup:
62+
connect: "jdbc:embed:/FRL/CROSS_SCHEMA_JOIN?schema=ITEMS_SCHEMA"
63+
steps:
64+
- query: INSERT INTO ITEMS VALUES (1, 'Apple'), (2, 'Banana'), (3, 'Cherry')
65+
---
66+
setup:
67+
connect: "jdbc:embed:/FRL/CROSS_SCHEMA_JOIN?schema=TAGS_SCHEMA"
68+
steps:
69+
- query: INSERT INTO TAGS VALUES (1, 'fruit'), (2, 'yellow'), (3, 'red')
70+
---
71+
setup:
72+
connect: "jdbc:embed:/FRL/CROSS_SCHEMA_JOIN?schema=PRICES_SCHEMA"
73+
steps:
74+
- query: INSERT INTO PRICES VALUES (1, 100), (2, 200), (3, 300)
75+
---
76+
setup:
77+
connect: "jdbc:embed:/FRL/CROSS_SCHEMA_JOIN?schema=DESCRIPTION_SCHEMA"
78+
steps:
79+
- query: INSERT INTO THINGS VALUES (1, 'Widget'), (2, 'Gadget'), (3, 'Doohickey')
80+
---
81+
setup:
82+
connect: "jdbc:embed:/FRL/CROSS_SCHEMA_JOIN?schema=QUANTITY_SCHEMA"
83+
steps:
84+
- query: INSERT INTO THINGS VALUES (1, 10), (2, 20), (3, 30)
85+
---
86+
test_block:
87+
connect: "jdbc:embed:/FRL/CROSS_SCHEMA_JOIN?schema=ITEMS_SCHEMA"
88+
preset: multi_repetition_ordered
89+
name: cross-schema-join-tests
90+
tests:
91+
-
92+
# Basic inner join: primary-schema table joined to a secondary-schema table.
93+
# The plan must contain STORE_BIND to route the inner scan to the secondary store.
94+
- query: SELECT items.name, tags.tag FROM ITEMS AS items
95+
JOIN TAGS_SCHEMA.TAGS AS tags ON items.id = tags.item_id
96+
ORDER BY items.id
97+
- explainContains: "STORE_BIND TAGS_SCHEMA"
98+
- result: [{'Apple', 'fruit'}, {'Banana', 'yellow'}, {'Cherry', 'red'}]
99+
-
100+
# Cross-schema join with a WHERE predicate on the outer (primary) table.
101+
- query: SELECT items.name, tags.tag FROM ITEMS AS items
102+
JOIN TAGS_SCHEMA.TAGS AS tags ON items.id = tags.item_id
103+
WHERE items.id = 2
104+
- result: [{'Banana', 'yellow'}]
105+
-
106+
# Standalone SELECT from a secondary schema — no join involved.
107+
- query: SELECT tag FROM TAGS_SCHEMA.TAGS ORDER BY item_id
108+
- explainContains: "STORE_BIND TAGS_SCHEMA"
109+
- result: [{'fruit'}, {'yellow'}, {'red'}]
110+
-
111+
# Three-schema join: primary + two secondaries in the same query.
112+
- query: SELECT items.name, tags.tag, prices.price
113+
FROM ITEMS AS items
114+
JOIN TAGS_SCHEMA.TAGS AS tags ON items.id = tags.item_id
115+
JOIN PRICES_SCHEMA.PRICES AS prices ON items.id = prices.item_id
116+
ORDER BY items.id
117+
- result: [{'Apple', 'fruit', !l 100}, {'Banana', 'yellow', !l 200}, {'Cherry', 'red', !l 300}]
118+
---
119+
test_block:
120+
# Connect as DESCRIPTION_SCHEMA; the THINGS table here has a "description" column.
121+
# QUANTITY_SCHEMA.THINGS has a "quantity" column. Both templates use the same table name,
122+
# which exercises type-namespace collision avoidance in the TypeRepository.
123+
connect: "jdbc:embed:/FRL/CROSS_SCHEMA_JOIN?schema=DESCRIPTION_SCHEMA"
124+
preset: multi_repetition_ordered
125+
name: cross-schema-type-collision-tests
126+
tests:
127+
-
128+
# Joining same-named tables from two schemas must not cause a TypeRepository collision.
129+
- query: SELECT a.description, b.quantity
130+
FROM THINGS AS a
131+
JOIN QUANTITY_SCHEMA.THINGS AS b ON a.id = b.id
132+
ORDER BY a.id
133+
- result: [{'Widget', !l 10}, {'Gadget', !l 20}, {'Doohickey', !l 30}]
134+
-
135+
# Standalone query from the primary schema's THINGS table.
136+
- query: SELECT description FROM THINGS ORDER BY id
137+
- result: [{'Widget'}, {'Gadget'}, {'Doohickey'}]
138+
-
139+
# Standalone query from the secondary schema's THINGS table.
140+
- query: SELECT quantity FROM QUANTITY_SCHEMA.THINGS ORDER BY id
141+
- result: [{!l 10}, {!l 20}, {!l 30}]

0 commit comments

Comments
 (0)