Skip to content

Prototype based on blackhole_fdw and c -> java calls #534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.postgresql.pljava.fdw;

/**
* Additional content for `EXPLAIN x...`
*
* No details yet.
*/
public interface FDWExplainState {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.postgresql.pljava.fdw;

import java.util.Collections;
import java.util.Map;

/**
* The Foreign Data Wrapper.
*
* This is the highest-level abstraction, e.g., for information
* contained in S3 files.
*
* It could also capture an abstract concept, e.g., one FDW
* to capture multiple authentication implementations.
*
* There may be multiple instances of a single FOREIGN DATA WRAPPER.
*/
public interface FDWForeignDataWrapper {

/**
* The instances unique ID. It should be used to maintain a cache.
* @return
*/
default Long getId() { return null; }

/**
* Return a copy of the options provided to `CREATE FOREIGN DATA WRAPPER...`
* @return
*/
default Map<String, String> getOptions() { return Collections.emptyMap(); };

/**
* Validate a set of options against an existing instance. There should be
* a similar static method before creating a new instance.
*
* @param options
* @return
*/
default boolean validateOptions(Map<String, String> options) { return true; };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this interface you have combined behaviors of a PostgreSQL catalog object (such as a getOptions method to report what options have been assigned in PostgreSQL DDL) with behaviors that belong to some chosen FDW implementation (such as a validateOptions method that knows which options are valid for that chosen implementation.

Those concerns are distinct. There should be (and already is) a catalog object interface that takes care of retrieving the foreign data wrapper's options and other particulars from the catalog. It's PL/Java's job to supply that and take care of the housekeeping, like caching it by its oid, making sure it isn't stale, and so on.

It's a particular FDW implementation's job to implement some different interface that specifies methods for validating the options, along with whatever other behavior an FDW implementation needs to supply.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package org.postgresql.pljava.fdw;

import java.sql.ResultSetMetaData;
import java.util.Collections;
import java.util.Map;

/**
* The Foreign Table
*
* This is the lowest-level abstraction, e.g., a specific
* S3 file.
*
* There may be multiple instances of a Foreign Table
* for a single Foreign Server.
*/
public interface FDWForeignTable {

/**
* The instances unique ID. It should be used to maintain a cache.
*/
default Long getId() { return null; }

/**
* Return a copy of the options provided to `CREATE FOREIGN TABLE...`
* @return
*/
default Map<String, String> getOptions() { return Collections.emptyMap(); };

/**
* Validate a set of options against an existing instance. There should be
* a similar static method before creating a new instance.
*
* @param options
* @return
*/
default boolean validateOptions(Map<String, String> options) { return true; };
Copy link
Contributor

@jcflack jcflack Jun 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for the wrapper validateOptions

Indeed, most of the methods of this interface are methods proper to an FDW implementation. It's only methods like getId and getOptions that duplicate the methods of the catalog object.


/**
* Create an object used for query planning.
*
* @param user
* @return
*/
FDWPlanState newPlanState(FDWUser user);

/**
* Create an object used for SELECT statements.
* @param user
* @return
*/
FDWScanState newScanState(FDWUser user, boolean explainOnly);

// values from PlannerInfo *root, RelOptInfo *baserel
// BUT NOT foreigntableoid
void getRelSize();

/*
static void blackholeGetForeignPaths(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid);
*/

// values from PlannerInfo *root, RelOptInfo *baserel
// BUT NOT foreigntableoid
void getForeignPaths();

/**
* Is this table updatable by this user?
*
* @param user
* @return
*/
default boolean isUupdatable(FDWUser user) { return false; }

/**
* Does this table support concurrent access?
* @return
*/
default boolean supportsConcurrency() { return false; }

/**
* Does this table support asynchronous queries?
* @return
*/
default boolean supportsAsyncOperations() { return false; }

/**
* Collect statistics used by the query optimizer.
* This can be supported for read-only tables.
*
* Details TBD
*/
default void analyze() { }

/**
* Compact the data, if appropriate.
* This should be a noop for read-only tables.
*
* Details TBD.
*/
default void vacuum() { }

/**
* Get the table's schema. This information
* will be used when executing `IMPORT FOREIGN SCHEMA...`
*
* @return
*/
default ResultSetMetaData getMetaData() { return null; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you assuming here that JDBC metadata will be adequate to describe the foreign table?

Are you assuming further that the foreign connection will involve JDBC? The JDBC ...MetaData interfaces will take many many lines of code to implement synthetically if it doesn't.


/**
* Estimate the number of rows.
*
* @return
*/
default long getRows() { return 0; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.postgresql.pljava.fdw;

public interface FDWPlanState {

// values from PlannerInfo *root, RelOptInfo *baserel.
// the PlannerInfo is only used in advanced queries.
void open();

void close();

default long getRows() { return 0; }

default FDWUser getUser() { return null; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.postgresql.pljava.fdw;

import java.util.Map;

public interface FDWScanState {
/**
* The database has already performed an initial check for
* the user's permission to execute SELECT - but our java
* code may impose additional requirements.
*
* The minimal implementation just adds the ability to check
* whether * the user is authorized. (The current FDWUser is
* transparently * passed to the appropriate method.)
*
* A more advanced implementation would allow us to
* add row and column filtering beyond what will already
* be done by the database.
*/
default boolean isAuthorizedUser() { return true; }

/**
* Verify that we have a valid configuration.
*
* No external resources should be accessed if
* the `explainOnly` flag is true. (It's okay to
* check a file exists and is readable but it should
* not be opened. A REST service can have its hostname
* verified but it should not be called.
*
* If the `explainOnly` flag is false than external
* resources can be accessed in order to verify
* that it's a valid URL and we have valid credentials.
* However all external resources should be released
* before this method exits.
*/
void open(boolean explainOnly);

// values from TableTupleType. It is an element
// of the ForeignScanState mentioned above.
Map<String, Object> next();

/**
* Reset scan to initial state.
*/
void reset();

/**
* Release resources
*/
void close();

default FDWExplainState explain() { return null; }

default FDWUser getUser() { return null; }
}
50 changes: 50 additions & 0 deletions pljava-api/src/main/java/org/postgresql/pljava/fdw/FDWServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.postgresql.pljava.fdw;

import java.sql.DatabaseMetaData;
import java.util.Collections;
import java.util.Map;

/**
* The Foreign Server
*
* This is the middle-level abstraction, e.g., a specific
* AWS account with access to the required resources.
*
* There may be multiple instances of a Foreign Server
* for a single Foreign Data Wrapper.
*/
public interface FDWServer {

/**
* The instances unique ID. It should be used to maintain a cache.
*/
default Long getId() { return null; }

/**
* Return a copy of the options provided to `CREATE FOREIGN SERVER...`
* @return
*/
default Map<String, String> getOptions() { return Collections.emptyMap(); };

/**
* Validate a set of options against an existing instance. There should be
* a similar static method before creating a new instance.
*
* @param options
* @return
*/
default boolean validateOptions(Map<String, String> options) { return true; };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for wrapper validateOptions.


/**
* Get the server's entire schema. This can be useful
* information even if the backend only gets the
* schema for individual tables.
*
* (It's not clear since the backend struct supports
* foreign keys but I don't think the individual
* ResultSetMetadata includes that information.)
*
* @return
*/
default DatabaseMetaData getMetaData() { return null; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for foreign table getMetaData.

}
Loading