Skip to content
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

Add force_readonly option for owner of a FOREIGN SERVER in context of SQLite connection management #78

Merged
merged 18 commits into from
May 13, 2024
Merged
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
2 changes: 1 addition & 1 deletion GitHubActions/detect_targets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

################################################################################
#
# This script detects target PostgreSQL versions for sqlite_fdw testing from
# This script detects target PostgreSQL versions for sqlite_fdw testing from
# directory names in ./sql directory. Detected versions will be outputed to
# the standard output as an array of string like ["15.4","16.0"].
#
Expand Down
2 changes: 1 addition & 1 deletion GitHubActions/install_locales.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# tests in Ubuntu.
#
# Usage: ./install_locales.sh
#
#
# Requirements:
# - having superuser privileges
#
Expand Down
141 changes: 87 additions & 54 deletions README.md

Large diffs are not rendered by default.

57 changes: 36 additions & 21 deletions connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ typedef struct ConnCacheEntry
bool keep_connections; /* setting value of keep_connections
* server option */
bool truncatable; /* check table can truncate or not */
bool readonly; /* option force_readonly, readonly SQLite file mode */
bool invalidated; /* true if reconnect is pending */
Oid serverid; /* foreign server OID used to get server name */
List *stmtList; /* list stmt associated with conn */
Expand All @@ -60,7 +61,7 @@ PG_FUNCTION_INFO_V1(sqlite_fdw_get_connections);
PG_FUNCTION_INFO_V1(sqlite_fdw_disconnect);
PG_FUNCTION_INFO_V1(sqlite_fdw_disconnect_all);

static sqlite3 *sqlite_open_db(const char *dbpath);
static sqlite3 *sqlite_open_db(const char *dbpath, int flags);
static void sqlite_make_new_connection(ConnCacheEntry *entry, ForeignServer *server);
void sqlite_do_sql_command(sqlite3 * conn, const char *sql, int level, List **busy_connection);
static void sqlite_begin_remote_xact(ConnCacheEntry *entry);
Expand Down Expand Up @@ -184,21 +185,21 @@ sqlite_get_connection(ForeignServer *server, bool truncatable)
}

/*
* Open remote sqlite database using specified database path.
* Open remote sqlite database using specified database path
* and flags of opened file descriptor mode.
*/
static sqlite3 *
sqlite_open_db(const char *dbpath)
sqlite_open_db(const char *dbpath, int flags)
{
sqlite3 *conn = NULL;
int rc;
char *err;

rc = sqlite3_open(dbpath, &conn);
const char *zVfs = NULL;
rc = sqlite3_open_v2(dbpath, &conn, flags, zVfs);
if (rc != SQLITE_OK)
ereport(ERROR,
(errcode(ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION),
errmsg("failed to open SQLite DB. rc=%d path=%s", rc, dbpath)));

errmsg("Failed to open SQLite DB, file '%s', result code %d", dbpath, rc)));
/* make 'LIKE' of SQLite case sensitive like PostgreSQL */
rc = sqlite3_exec(conn, "pragma case_sensitive_like=1",
NULL, NULL, &err);
Expand All @@ -208,9 +209,10 @@ sqlite_open_db(const char *dbpath)

sqlite3_free(err);
sqlite3_close(conn);
conn = NULL;
ereport(ERROR,
(errcode(ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION),
errmsg("failed to open SQLite DB. rc=%d err=%s", rc, perr)));
errmsg("Failed to open SQLite DB, file '%s', SQLite error '%s', result code %d", dbpath, perr, rc)));
}
/* add included inner SQLite functions from separate c file
* for using in data unifying during deparsing
Expand All @@ -219,6 +221,7 @@ sqlite_open_db(const char *dbpath)
return conn;
}


/*
* Reset all transient state fields in the cached connection entry and
* establish new connection to the remote server.
Expand All @@ -228,6 +231,7 @@ sqlite_make_new_connection(ConnCacheEntry *entry, ForeignServer *server)
{
const char *dbpath = NULL;
ListCell *lc;
int flags = 0;

Assert(entry->conn == NULL);

Expand All @@ -236,6 +240,7 @@ sqlite_make_new_connection(ConnCacheEntry *entry, ForeignServer *server)
entry->invalidated = false;
entry->stmtList = NULL;
entry->keep_connections = true;
entry->readonly = false;
entry->server_hashvalue =
GetSysCacheHashValue1(FOREIGNSERVEROID,
ObjectIdGetDatum(server->serverid));
Expand All @@ -247,10 +252,13 @@ sqlite_make_new_connection(ConnCacheEntry *entry, ForeignServer *server)
dbpath = defGetString(def);
else if (strcmp(def->defname, "keep_connections") == 0)
entry->keep_connections = defGetBoolean(def);
else if (strcmp(def->defname, "force_readonly") == 0)
entry->readonly = defGetBoolean(def);
}

flags = flags | (entry->readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE);
/* Try to make the connection */
entry->conn = sqlite_open_db(dbpath);
entry->conn = sqlite_open_db(dbpath, flags);
}

/*
Expand Down Expand Up @@ -282,8 +290,9 @@ sqlite_cleanup_connection(void)
{
ereport(ERROR,
(errcode(ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION),
errmsg("close connection failed: %s rc=%d", sqlite3_errmsg(entry->conn), rc)
));
errmsg("Failed to close SQLite DB"),
errhint("SQLite error '%s', SQLite result code %d", sqlite3_errmsg(entry->conn), rc)
));
}
}
}
Expand Down Expand Up @@ -327,15 +336,18 @@ sqlite_do_sql_command(sqlite3 * conn, const char *sql, int level, List **busy_co
{
ereport(level,
(errcode(ERRCODE_FDW_ERROR),
errmsg("SQLite failed to execute sql: %s %s", sql, perr)
));
errmsg("SQLite failed to execute a query"),
errcontext("SQL query: %s", sql),
errhint("SQLite error '%s'", perr)));

pfree(perr);
}
}
else
ereport(level,
(errcode(ERRCODE_FDW_ERROR),
errmsg("SQLite failed to execute sql: %s", sql)
errmsg("SQLite failed to execute a query"),
errcontext("SQL query: %s", sql)
));
}
}
Expand Down Expand Up @@ -401,10 +413,10 @@ sqlitefdw_report_error(int elevel, sqlite3_stmt * stmt, sqlite3 * conn,
}
ereport(ERROR,
(errcode(sqlstate),
errmsg("failed to execute remote SQL: rc=%d %s \n sql=%s",
rc, message ? message : "", sql ? sql : "")
));

errmsg("Failed to execute remote SQL"),
errcontext("SQL query: %s", sql ? sql : ""),
errhint("SQLite error '%s', SQLite result code %d", message ? message : "", rc)
));
}


Expand Down Expand Up @@ -903,9 +915,12 @@ sqlitefdw_abort_cleanup(ConnCacheEntry *entry, bool toplevel, List **busy_connec
{
char sql[100];
int curlevel = GetCurrentTransactionNestLevel();
snprintf(sql, sizeof(sql),
"ROLLBACK TO SAVEPOINT s%d; RELEASE SAVEPOINT s%d",
curlevel, curlevel);
snprintf(sql,
sizeof(sql),
"ROLLBACK TO SAVEPOINT s%d; RELEASE SAVEPOINT s%d",
curlevel,
curlevel
);
if (!sqlite3_get_autocommit(entry->conn))
sqlite_do_sql_command(entry->conn, sql, ERROR, busy_connection);
}
Expand Down
5 changes: 3 additions & 2 deletions expected/12.16/extra/bool.out
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,9 @@ INSERT INTO "type_BOOLEANpk" VALUES (TRUE);
INSERT INTO "type_BOOLEANpk" VALUES (FALSE);
--Testcase 44: ERR - primary key
INSERT INTO "type_BOOLEANpk" VALUES (TRUE);
ERROR: failed to execute remote SQL: rc=19 UNIQUE constraint failed: type_BOOLEANpk.col
sql=INSERT INTO main."type_BOOLEANpk"(`col`) VALUES (?)
ERROR: Failed to execute remote SQL
HINT: SQLite error 'UNIQUE constraint failed: type_BOOLEANpk.col', SQLite result code 19
CONTEXT: SQL query: INSERT INTO main."type_BOOLEANpk"(`col`) VALUES (?)
--Testcase 45:
DELETE FROM "type_BOOLEANpk";
--Testcase 46:
Expand Down
5 changes: 3 additions & 2 deletions expected/12.16/extra/insert.out
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ OPTIONS (database '/tmp/sqlite_fdw_test/core.db');
CREATE FOREIGN TABLE inserttest01 (col1 int4, col2 int4 NOT NULL, col3 text default 'testing') SERVER sqlite_svr;
--Testcase 1:
insert into inserttest01 (col1, col2, col3) values (DEFAULT, DEFAULT, DEFAULT);
ERROR: failed to execute remote SQL: rc=19 NOT NULL constraint failed: inserttest01.col2
sql=INSERT INTO main."inserttest01"(`col1`, `col2`, `col3`) VALUES (?, ?, ?)
ERROR: Failed to execute remote SQL
HINT: SQLite error 'NOT NULL constraint failed: inserttest01.col2', SQLite result code 19
CONTEXT: SQL query: INSERT INTO main."inserttest01"(`col1`, `col2`, `col3`) VALUES (?, ?, ?)
--Testcase 2:
insert into inserttest01 (col2, col3) values (3, DEFAULT);
--Testcase 3:
Expand Down
27 changes: 16 additions & 11 deletions expected/12.16/extra/sqlite_fdw_post.out
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ SELECT c3, c4 FROM ft1 ORDER BY c3, c1 LIMIT 1; -- should work
ALTER SERVER sqlite_svr OPTIONS (SET database 'no such database');
--Testcase 7:
SELECT c3, c4 FROM ft1 ORDER BY c3, c1 LIMIT 1; -- should fail
ERROR: SQL error during prepare: no such table: main.T 1 SELECT `C 1`, `c3`, `c4` FROM main."T 1" ORDER BY `c3` ASC NULLS LAST, `C 1` ASC NULLS LAST LIMIT 1
ERROR: Failed to open SQLite DB, file 'no such database', result code 14
DO $d$
BEGIN
EXECUTE $$ALTER SERVER sqlite_svr
Expand Down Expand Up @@ -6424,8 +6424,9 @@ ALTER FOREIGN TABLE ft1 RENAME TO ft1_org;
ALTER FOREIGN TABLE ft1_constraint RENAME TO ft1;
--Testcase 319:
INSERT INTO ft1(c1, c2) VALUES(11, 12); -- duplicate key
ERROR: failed to execute remote SQL: rc=19 UNIQUE constraint failed: t1_constraint.c1
sql=INSERT INTO main."t1_constraint"(`c1`, `c2`, `c3`, `c4`, `c5`, `c6`, `c7`, `c8`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ERROR: Failed to execute remote SQL
HINT: SQLite error 'UNIQUE constraint failed: t1_constraint.c1', SQLite result code 19
CONTEXT: SQL query: INSERT INTO main."t1_constraint"(`c1`, `c2`, `c3`, `c4`, `c5`, `c6`, `c7`, `c8`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
--Testcase 320:
INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) DO NOTHING; -- unsupported
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
Expand All @@ -6434,12 +6435,14 @@ INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) DO UPDATE SET c3 = '
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
--Testcase 743:
INSERT INTO ft1(c1, c2) VALUES(1111, -2); -- c2positive
ERROR: failed to execute remote SQL: rc=19 CHECK constraint failed: c2 >= 0
sql=INSERT INTO main."t1_constraint"(`c1`, `c2`, `c3`, `c4`, `c5`, `c6`, `c7`, `c8`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ERROR: Failed to execute remote SQL
HINT: SQLite error 'CHECK constraint failed: c2 >= 0', SQLite result code 19
CONTEXT: SQL query: INSERT INTO main."t1_constraint"(`c1`, `c2`, `c3`, `c4`, `c5`, `c6`, `c7`, `c8`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
--Testcase 744:
UPDATE ft1 SET c2 = -c2 WHERE c1 = 1; -- c2positive
ERROR: failed to execute remote SQL: rc=19 CHECK constraint failed: c2 >= 0
sql=UPDATE main."t1_constraint" SET `c2` = (- `c2`) WHERE ((`c1` = 1))
ERROR: Failed to execute remote SQL
HINT: SQLite error 'CHECK constraint failed: c2 >= 0', SQLite result code 19
CONTEXT: SQL query: UPDATE main."t1_constraint" SET `c2` = (- `c2`) WHERE ((`c1` = 1))
--Testcase 750:
ALTER FOREIGN TABLE ft1 RENAME TO ft1_constraint;
--Testcase 751:
Expand Down Expand Up @@ -6840,12 +6843,14 @@ RESET constraint_exclusion;
-- check constraint is enforced on the remote side, not locally
--Testcase 745:
INSERT INTO ft1(c1, c2) VALUES(1111, -2); -- c2positive
ERROR: failed to execute remote SQL: rc=19 CHECK constraint failed: c2 >= 0
sql=INSERT INTO main."t1_constraint"(`c1`, `c2`, `c3`, `c4`, `c5`, `c6`, `c7`, `c8`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ERROR: Failed to execute remote SQL
HINT: SQLite error 'CHECK constraint failed: c2 >= 0', SQLite result code 19
CONTEXT: SQL query: INSERT INTO main."t1_constraint"(`c1`, `c2`, `c3`, `c4`, `c5`, `c6`, `c7`, `c8`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
--Testcase 746:
UPDATE ft1 SET c2 = -c2 WHERE c1 = 1; -- c2positive
ERROR: failed to execute remote SQL: rc=19 CHECK constraint failed: c2 >= 0
sql=UPDATE main."t1_constraint" SET `c2` = (- `c2`) WHERE ((`c1` = 1))
ERROR: Failed to execute remote SQL
HINT: SQLite error 'CHECK constraint failed: c2 >= 0', SQLite result code 19
CONTEXT: SQL query: UPDATE main."t1_constraint" SET `c2` = (- `c2`) WHERE ((`c1` = 1))
ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2positive;
-- But inconsistent check constraints provide inconsistent results
ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2negative CHECK (c2 < 0);
Expand Down
5 changes: 3 additions & 2 deletions expected/12.16/extra/uuid.out
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,9 @@ SELECT * FROM "type_UUIDpk";

--Testcase 105: ERR - primary key
INSERT INTO "type_UUIDpk" VALUES ('{b0eebc99-9c0b4ef8-bb6d6bb9-bd380a12}');
ERROR: failed to execute remote SQL: rc=19 UNIQUE constraint failed: type_UUIDpk.col
sql=INSERT INTO main."type_UUIDpk"(`col`) VALUES (?)
ERROR: Failed to execute remote SQL
HINT: SQLite error 'UNIQUE constraint failed: type_UUIDpk.col', SQLite result code 19
CONTEXT: SQL query: INSERT INTO main."type_UUIDpk"(`col`) VALUES (?)
--Testcase 106:
ALTER FOREIGN TABLE "type_UUIDpk" ALTER COLUMN col OPTIONS (SET column_type 'BLOB');
--Testcase 107: NO ERR, but the same semantics!
Expand Down
Loading