Skip to content

Commit 94a6666

Browse files
author
qxip
committed
ATTACH feature
1 parent e20a93b commit 94a6666

File tree

4 files changed

+109
-0
lines changed

4 files changed

+109
-0
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,30 @@ GIGAPI SELECT * FROM my_measurement WHERE time > now() - interval '1 hour';
8383

8484
Behind the scenes, the extension performs the same steps as the `gigapi()` table function, rewriting the query to read from specific data files before execution. If a query is not prefixed with `GIGAPI`, it will be handled by DuckDB's default planner.
8585

86+
## ATTACH Support for GigAPI
87+
88+
The extension supports DuckDB's `ATTACH` statement, allowing you to create a virtual schema that pipes all table references through the GigAPI metadata engine. This enables seamless integration with tools and workflows that expect standard DuckDB schemas and tables.
89+
90+
### Usage
91+
92+
```sql
93+
-- Attach a GigAPI database as schema 'bq'
94+
ATTACH 'mydb' AS bq (TYPE gigapi, READ_ONLY);
95+
96+
-- List all tables in the attached GigAPI schema
97+
SHOW TABLES;
98+
99+
-- Query a table from the attached schema
100+
SELECT * FROM bq.example;
101+
```
102+
103+
**How it works:**
104+
- Any table reference in the attached schema (e.g., `bq.example`) is transparently routed through the `gigapi()` table function.
105+
- You do not need to manually create views or enumerate tables; the extension handles all resolution dynamically.
106+
- Metadata queries (e.g., `SHOW TABLES`, `DESCRIBE`, etc.) are also supported and routed through GigAPI.
107+
108+
This makes it easy to use GigAPI-backed data as if it were a native DuckDB database.
109+
86110
## Developer Information
87111

88112
### Dry Run Function

src/gigapi_attach.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include "include/gigapi_attach.hpp"
2+
#include "duckdb/catalog/catalog.hpp"
3+
#include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp"
4+
#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
5+
#include "duckdb/main/extension_util.hpp"
6+
#include "duckdb/main/database.hpp"
7+
#include "duckdb/parser/parser.hpp"
8+
#include "duckdb/planner/binder.hpp"
9+
#include "duckdb/parser/statement/select_statement.hpp"
10+
#include "duckdb/common/exception.hpp"
11+
#include "duckdb/common/make_uniq.hpp"
12+
13+
namespace duckdb {
14+
15+
// Table entry that pipes through gigapi()
16+
class GigapiTableEntry : public TableCatalogEntry {
17+
public:
18+
GigapiTableEntry(Catalog &catalog, SchemaCatalogEntry &schema, const std::string &name)
19+
: TableCatalogEntry(CatalogType::TABLE_ENTRY, schema, name) {}
20+
21+
unique_ptr<LogicalOperator> GetScanOperator(ClientContext &context) override {
22+
// Build: SELECT * FROM gigapi('schema.table')
23+
std::string qualified = schema.name + "." + name;
24+
std::string query = "SELECT * FROM gigapi('" + qualified + "')";
25+
Parser parser;
26+
parser.ParseQuery(query);
27+
if (parser.statements.empty() || parser.statements[0]->type != StatementType::SELECT_STATEMENT) {
28+
throw BinderException("Failed to parse gigapi table scan query");
29+
}
30+
Binder binder(context);
31+
auto bound = binder.Bind(*parser.statements[0]);
32+
return std::move(bound.plan);
33+
}
34+
};
35+
36+
GigapiSchema::GigapiSchema(Catalog &catalog, const std::string &name)
37+
: SchemaCatalogEntry(catalog, name) {}
38+
39+
TableCatalogEntry *GigapiSchema::GetTable(ClientContext &context, const std::string &table_name) {
40+
// Always return a GigapiTableEntry for any table name
41+
return new GigapiTableEntry(*catalog, *this, table_name);
42+
}
43+
44+
// Handler for ATTACH ... (TYPE gigapi, ...)
45+
static void GigapiAttachHandler(ClientContext &context, const std::string &schema_name) {
46+
auto &catalog = Catalog::GetSystemCatalog(context);
47+
CreateSchemaInfo info;
48+
info.schema = schema_name;
49+
info.if_not_exists = true;
50+
catalog.CreateSchema(context, info);
51+
52+
// Replace the schema with our custom schema
53+
auto &db = DatabaseInstance::Get(context);
54+
auto &schemas = db.GetCatalogSet(CatalogType::SCHEMA_ENTRY);
55+
schemas.DropEntry(context, schema_name, false);
56+
auto gigapi_schema = make_uniq<GigapiSchema>(catalog, schema_name);
57+
schemas.AddEntry(context, std::move(gigapi_schema));
58+
}
59+
60+
void RegisterGigapiAttach(DatabaseInstance &instance) {
61+
// Register the attach handler for TYPE gigapi
62+
ExtensionUtil::RegisterAttachHandler(instance, "gigapi", GigapiAttachHandler);
63+
}
64+
65+
} // namespace duckdb

src/gigapi_extension.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "duckdb/catalog/catalog_transaction.hpp"
3939
#include "duckdb/common/pair.hpp"
4040

41+
#include "include/gigapi_attach.hpp"
4142

4243
namespace duckdb {
4344

@@ -610,6 +611,8 @@ static void LoadInternal(DatabaseInstance &instance) {
610611

611612
auto giga_test_create_empty_index_scalar = ScalarFunction("giga_test_create_empty_index", {LogicalType::VARCHAR}, LogicalType::BOOLEAN, GigapiTestCreateEmptyIndexFunction);
612613
ExtensionUtil::RegisterFunction(instance, giga_test_create_empty_index_scalar);
614+
615+
RegisterGigapiAttach(instance);
613616
}
614617

615618
void GigapiExtension::Load(DuckDB &db) {

src/include/gigapi_attach.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
#include "duckdb.hpp"
4+
#include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp"
5+
6+
namespace duckdb {
7+
8+
class GigapiSchema : public SchemaCatalogEntry {
9+
public:
10+
GigapiSchema(Catalog &catalog, const std::string &name);
11+
12+
TableCatalogEntry *GetTable(ClientContext &context, const std::string &table_name) override;
13+
};
14+
15+
void RegisterGigapiAttach(DatabaseInstance &instance);
16+
17+
} // namespace duckdb

0 commit comments

Comments
 (0)