summaryrefslogtreecommitdiff
path: root/lang/sql/adapter/db_pragma.c
diff options
context:
space:
mode:
Diffstat (limited to 'lang/sql/adapter/db_pragma.c')
-rw-r--r--lang/sql/adapter/db_pragma.c386
1 files changed, 373 insertions, 13 deletions
diff --git a/lang/sql/adapter/db_pragma.c b/lang/sql/adapter/db_pragma.c
index 4c70afec..e7f8fa0e 100644
--- a/lang/sql/adapter/db_pragma.c
+++ b/lang/sql/adapter/db_pragma.c
@@ -1,7 +1,7 @@
/*-
* See the file LICENSE for redistribution information.
*
- * Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015 Oracle and/or its affiliates. All rights reserved.
*/
/*
@@ -11,7 +11,6 @@
#include "btreeInt.h"
extern void returnSingleInt(Parse *, const char *, i64);
-extern u8 getBoolean(const char *);
extern int __os_exists (ENV *, const char *, int *);
extern int __os_unlink (ENV *, const char *, int);
extern int __os_mkdir (ENV *, const char *, int);
@@ -23,6 +22,16 @@ extern int __env_ref_get (DB_ENV *, u_int32_t *);
static const char *PRAGMA_FILE = "pragma";
static const char *PRAGMA_VERSION = "1.0";
+static const char *ACK_POLICY_ALL = "all_sites";
+static const char *ACK_POLICY_ALL_AVAILABLE = "all_available";
+static const char *ACK_POLICY_NONE = "none";
+static const char *ACK_POLICY_ONE = "one";
+static const char *ACK_POLICY_QUORUM = "quorum";
+
+static const char *REP_SITE_MASTER = "MASTER";
+static const char *REP_SITE_CLIENT = "CLIENT";
+static const char *REP_SITE_UNKNOWN = "UNKNOWN";
+
static const u32 HDR_SIZE = 256;
static const u32 RECORD_HDR_SIZE = 8;
static const u32 VERSION_RECORD_SIZE = 12;
@@ -47,6 +56,57 @@ static const u32 DEFINED_PRAGMAS = 8;
#define dbExists (pDb->pBt->pBt->full_name != NULL && \
!__os_exists(NULL, pDb->pBt->pBt->full_name, NULL))
+/* Translates a text ack policy to its DB value. */
+static int textToAckPolicy(const char *policy)
+{
+ int len;
+ if (policy == NULL)
+ return (-1);
+
+ len = (int)strlen(policy);
+
+ if ((sqlite3StrNICmp(policy, ACK_POLICY_ALL, len)) == 0)
+ return (DB_REPMGR_ACKS_ALL);
+ else if ((sqlite3StrNICmp(policy, ACK_POLICY_ALL_AVAILABLE, len)) == 0)
+ return (DB_REPMGR_ACKS_ALL_AVAILABLE);
+ else if ((sqlite3StrNICmp(policy, ACK_POLICY_NONE, len)) == 0)
+ return (DB_REPMGR_ACKS_NONE);
+ else if ((sqlite3StrNICmp(policy, ACK_POLICY_ONE, len)) == 0)
+ return (DB_REPMGR_ACKS_ONE);
+ else if ((sqlite3StrNICmp(policy, ACK_POLICY_QUORUM, len)) == 0)
+ return (DB_REPMGR_ACKS_QUORUM);
+ else
+ return (-1);
+}
+
+/* Translates a DB value ack policy to text. */
+static const char *ackPolicyToText(int policy)
+{
+ if (policy == DB_REPMGR_ACKS_ALL)
+ return (ACK_POLICY_ALL);
+ else if (policy == DB_REPMGR_ACKS_ALL_AVAILABLE)
+ return (ACK_POLICY_ALL_AVAILABLE);
+ else if (policy == DB_REPMGR_ACKS_NONE)
+ return (ACK_POLICY_NONE);
+ else if (policy == DB_REPMGR_ACKS_ONE)
+ return (ACK_POLICY_ONE);
+ else if (policy == DB_REPMGR_ACKS_QUORUM)
+ return (ACK_POLICY_QUORUM);
+ else
+ return (NULL);
+}
+
+/* Translates a replication site type to text. */
+static const char *repSiteTypeToText(rep_site_type_t type)
+{
+ if (type == BDBSQL_REP_MASTER)
+ return (REP_SITE_MASTER);
+ else if (type == BDBSQL_REP_CLIENT)
+ return (REP_SITE_CLIENT);
+ else
+ return (REP_SITE_UNKNOWN);
+}
+
static u8 envIsClosed(Parse *pParse, Btree *p, const char *pragmaName)
{
int rc;
@@ -82,6 +142,12 @@ int bdbsqlPragmaMultiversion(Parse *pParse, Btree *p, u8 on)
return 1;
pBt = p->pBt;
+ if (pBt->blobs_enabled && on) {
+ sqlite3ErrorMsg(pParse,
+ "Cannot enable both multiversion and large record optimization.");
+ return 1;
+ }
+
/* Do not want other processes opening the environment */
mutexOpen = sqlite3MutexAlloc(OPEN_MUTEX(pBt->dbStorage));
sqlite3_mutex_enter(mutexOpen);
@@ -250,15 +316,29 @@ static int bdbsqlPragmaStartReplication(Parse *pParse, Db *pDb)
}
if (dbExists) {
+ if (!pBt->pBt->env_opened) {
+ if ((rc = btreeOpenEnvironment(pBt, 1)) != SQLITE_OK)
+ sqlite3ErrorMsg(pParse, "Could not start "
+ "replication on an existing database");
+ goto done;
+ }
+
/*
- * Turning on replication requires recovery on the underlying
+ * Opening the environment started repmgr if it was
+ * configured for it, so we are done here.
+ */
+ if (supportsReplication(pBt))
+ goto done;
+ /*
+ * Turning on replication on an existing environment not
+ * configured for replication requires recovery on the
* BDB environment, which requires single-threaded access.
* Make sure there are no other processes or threads accessing
* it. This is not foolproof, because there is a small chance
* that another process or thread could slip in there between
* this call and the recovery, but it should cover most cases.
*/
- if (hasDatabaseConnections(pBt)) {
+ if (hasDatabaseConnections(pBt) || pBt->pBt->nRef > 1) {
sqlite3ErrorMsg(pParse, "Close all database "
"connections before turning on replication");
goto done;
@@ -275,7 +355,7 @@ static int bdbsqlPragmaStartReplication(Parse *pParse, Db *pDb)
* the underlying BDB environment with replication always
* performs a recovery.
*/
- pBt->pBt->repStartMaster = 1;
+ pBt->pBt->repForceRecover = 1;
if ((rc = btreeReopenEnvironment(pBt, 0)) != SQLITE_OK)
sqlite3ErrorMsg(pParse, "Could not "
"start replication on an existing database");
@@ -310,6 +390,7 @@ done:
static int bdbsqlPragmaStopReplication(Parse *pParse, Db *pDb)
{
Btree *pBt;
+ char *old_addr = NULL;
int rc = SQLITE_OK;
pBt = pDb->pBt;
@@ -341,6 +422,13 @@ static int bdbsqlPragmaStopReplication(Parse *pParse, Db *pDb)
*/
pBt->pBt->repForceRecover = 1;
rc = btreeReopenEnvironment(pBt, 1);
+ sqlite3_mutex_enter(pBt->pBt->mutex);
+ pBt->pBt->repRole = BDBSQL_REP_UNKNOWN;
+ old_addr = pBt->pBt->master_address;
+ pBt->pBt->master_address = NULL;
+ sqlite3_mutex_leave(pBt->pBt->mutex);
+ if (old_addr)
+ sqlite3_free(old_addr);
done:
return rc;
@@ -383,14 +471,14 @@ int bdbsqlPragma(Parse *pParse, char *zLeft, char *zRight, int iDb)
}
} else if (sqlite3StrNICmp(zLeft, "txn_bulk", 8) == 0) {
if (zRight)
- pBt->txn_bulk = getBoolean(zRight);
+ pBt->txn_bulk = sqlite3GetBoolean(zRight, 0);
returnSingleInt(pParse, "txn_bulk", (i64)pBt->txn_bulk);
parsed = 1;
/* Enables MVCC and transactions snapshots. */
} else if (sqlite3StrNICmp(zLeft, "multiversion", 12) == 0) {
if (zRight)
bdbsqlPragmaMultiversion(pParse, pDb->pBt,
- getBoolean(zRight));
+ sqlite3GetBoolean(zRight, 0));
returnSingleInt(pParse, "multiversion",
(i64)((pDb->pBt->pBt->env_oflags & DB_MULTIVERSION)?
@@ -401,7 +489,7 @@ int bdbsqlPragma(Parse *pParse, char *zLeft, char *zRight, int iDb)
*/
} else if (sqlite3StrNICmp(zLeft, "snapshot_isolation", 18) == 0) {
if (zRight) {
- if (getBoolean(zRight)) {
+ if (sqlite3GetBoolean(zRight, 0)) {
if (pDb->pBt->pBt->env_oflags &
DB_MULTIVERSION) {
pDb->pBt->pBt->read_txn_flags
@@ -530,7 +618,7 @@ int bdbsqlPragma(Parse *pParse, char *zLeft, char *zRight, int iDb)
setValue[0] = '\0';
startedRep = stoppedRep = turningOn = 0;
if (zRight) {
- turningOn = (getBoolean(zRight) == 1);
+ turningOn = (sqlite3GetBoolean(zRight, 0) == 1);
strcpy(setValue, turningOn ? "1" : "0");
rc = setPersistentPragma(pDb->pBt, zLeft,
setValue, pParse);
@@ -596,7 +684,7 @@ int bdbsqlPragma(Parse *pParse, char *zLeft, char *zRight, int iDb)
outValue[0] = '\0';
if (zRight)
pDb->pBt->pBt->repStartMaster =
- getBoolean(zRight) == 1 ? 1 : 0;
+ sqlite3GetBoolean(zRight, 0) == 1 ? 1 : 0;
strcpy(outValue,
pDb->pBt->pBt->repStartMaster == 1 ? "1" : "0");
sqlite3VdbeSetNumCols(pParse->pVdbe, 1);
@@ -733,6 +821,40 @@ int bdbsqlPragma(Parse *pParse, char *zLeft, char *zRight, int iDb)
pDb->pBt->txn_priority);
parsed = 1;
/*
+ * PRAGMA bdbsql_log_buffer; -- DB_ENV->get_lg_bsize
+ * PRAGMA bdbsql_log_buffer = N; -- DB_ENV->set_lg_bsize
+ * N provides the size of the log buffer size in bytes.
+ */
+ } else if (sqlite3StrNICmp(zLeft, "bdbsql_log_buffer", 17) == 0) {
+ int iLimit = -2;
+ u_int32_t val;
+
+ if (zRight &&
+ envIsClosed(pParse, pDb->pBt, "bdbsql_log_buffer")) {
+ if (!sqlite3GetInt32(zRight, &iLimit) ||
+ iLimit < 0)
+ sqlite3ErrorMsg(pParse,
+ "Invalid value bdbsql_log_buffer %s",
+ zRight);
+ else {
+ val = iLimit;
+ if ((ret =
+ pDb->pBt->pBt->dbenv->set_lg_bsize(
+ pDb->pBt->pBt->dbenv, val)) != 0) {
+ sqlite3ErrorMsg(pParse,
+ "Failed to set log buffer size "
+ "error: %d.", ret);
+ }
+ }
+ } else if (zRight == NULL) {
+ /* Get existing value */
+ pDb->pBt->pBt->dbenv->get_lg_bsize(
+ pDb->pBt->pBt->dbenv, &val);
+
+ returnSingleInt(pParse, "bdbsql_log_buffer", val);
+ }
+ parsed = 1;
+ /*
* PRAGMA bdbsql_single_process = boolean;
* Turn on/off omit sharing.
*/
@@ -743,7 +865,7 @@ int bdbsqlPragma(Parse *pParse, char *zLeft, char *zRight, int iDb)
if (zRight &&
envIsClosed(pParse, pDb->pBt, "bdbsql_single_process")) {
- new_value = getBoolean(zRight);
+ new_value = sqlite3GetBoolean(zRight, 0);
if (new_value != pDb->pBt->pBt->single_process)
is_changed = 1;
}
@@ -868,7 +990,7 @@ int bdbsqlPragma(Parse *pParse, char *zLeft, char *zRight, int iDb)
if (zRight &&
envIsClosed(pParse, pDb->pBt, "bdbsql_shared_resources")) {
- if (pDb->pBt->pBt->need_open) {
+ if (pDb->pBt->pBt->database_existed) {
/*
* The DBENV->set_memory_max() method must be
* called prior to opening/creating the database
@@ -918,7 +1040,7 @@ int bdbsqlPragma(Parse *pParse, char *zLeft, char *zRight, int iDb)
if (zRight &&
envIsClosed(pParse, pDb->pBt, "bdbsql_lock_tablesize")) {
- if (pDb->pBt->pBt->need_open) {
+ if (pDb->pBt->pBt->database_existed) {
/*
* The DB_ENV->set_lk_tablesize() method must be
* called prior to opening/creating the database
@@ -949,6 +1071,244 @@ int bdbsqlPragma(Parse *pParse, char *zLeft, char *zRight, int iDb)
returnSingleInt(pParse, "bdbsql_lock_tablesize", val);
parsed = 1;
+ /*
+ * PRAGMA large_record_opt; -- Gets the blob threshold
+ * PRAGMA large_record_opt = N; -- Sets the blob threshold of all
+ * tables opened in the database. The blob threshold is set to
+ * N. A value of 0 disables blobs.
+ */
+ } else if (sqlite3StrNICmp(zLeft, "large_record_opt", 16) == 0) {
+ int iLimit = -2;
+ u_int32_t val;
+
+ if (zRight) {
+ if (!sqlite3GetInt32(zRight, &iLimit) || iLimit < 0) {
+ sqlite3ErrorMsg(pParse,
+ "Invalid value large_record_opt %s",
+ zRight);
+ } else {
+ /*
+ * SQL only supports records up to 1GB in
+ * size, so reject page counts that result
+ * in a blob threshold that will always be
+ * larger than 1 GB, to prevent overflow.
+ */
+ if (iLimit > GIGABYTE) {
+ sqlite3ErrorMsg(pParse,
+ "large_record_opt must be less than or equal to 1 gigabyte",
+ zRight);
+ } else {
+ pDb->pBt->pBt->blob_threshold
+ = iLimit;
+ if (iLimit > 0) {
+ pDb->pBt->pBt->blobs_enabled
+ = 1;
+ }
+ }
+ }
+ }
+
+ val = pDb->pBt->pBt->blob_threshold;
+ returnSingleInt(pParse, "large_record_opt", val);
+ parsed = 1;
+ /*
+ * PRAGMA replication_ack_policy; -- DB_ENV->repmgr_get_ack_policy
+ * PRAGMA replication_ack_policy =
+ * [all_sites|all_available|none|one|quorum];
+ * -- DB_ENV->repmgr_set_ack_policy
+ * Sets the policy on how many clients must acknowledge a transaction
+ * commit message before the master will consider it durable.
+ */
+ } else if (sqlite3StrNICmp(zLeft, "replication_ack_policy", 22) == 0) {
+ int val = -1;
+ const char *policy;
+
+ if (zRight) {
+ if ((val = textToAckPolicy(zRight)) == -1)
+ sqlite3ErrorMsg(pParse,
+ "Invalid value replication_ack_policy %s",
+ zRight);
+ else {
+ if ((ret =
+ pDb->pBt->pBt->dbenv->repmgr_set_ack_policy(
+ pDb->pBt->pBt->dbenv, val)) != 0) {
+ sqlite3ErrorMsg(pParse,
+ "Failed to set "
+ "replication_ack_policy. "
+ "Error: %d.", ret);
+ }
+ }
+ }
+
+ /* Get existing value */
+ pDb->pBt->pBt->dbenv->repmgr_get_ack_policy(
+ pDb->pBt->pBt->dbenv, &val);
+
+ policy = ackPolicyToText(val);
+ sqlite3VdbeSetNumCols(pParse->pVdbe, 1);
+ sqlite3VdbeSetColName(pParse->pVdbe, 0, COLNAME_NAME,
+ zLeft, SQLITE_STATIC);
+ if (policy != NULL)
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_String8, 0, 1, 0,
+ policy, 0);
+ else
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_String8, 0, 1, 0,
+ "No policy", 0);
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_ResultRow, 1, 1);
+ parsed = 1;
+ /*
+ * PRAGMA replication_ack_timeout; -- DB_ENV->rep_get_timeout
+ * PRAGMA replication_ack_timeout = N; -- DB_ENV->rep_set_timeout
+ * N provides the greater than -1 timeout on acks from clients
+ * when the master sends a transaction commit message.
+ */
+ } else if
+ (sqlite3StrNICmp(zLeft, "replication_ack_timeout", 23) == 0) {
+ int iLimit = -2;
+ db_timeout_t val;
+
+ if (zRight) {
+ if (!sqlite3GetInt32(zRight, &iLimit) || iLimit < 0)
+ sqlite3ErrorMsg(pParse,
+ "Invalid value replication_ack_timeout %s",
+ zRight);
+ else {
+ val = iLimit;
+ if ((ret =
+ pDb->pBt->pBt->dbenv->rep_set_timeout(
+ pDb->pBt->pBt->dbenv, DB_REP_ACK_TIMEOUT,
+ val)) != 0) {
+ sqlite3ErrorMsg(pParse,
+ "Failed to set "
+ "replication_ack_timeout. "
+ "Error: %d.", ret);
+ }
+ }
+ }
+
+ /* Get existing value */
+ pDb->pBt->pBt->dbenv->rep_get_timeout(
+ pDb->pBt->pBt->dbenv, DB_REP_ACK_TIMEOUT, &val);
+
+ returnSingleInt(pParse, "replication_ack_timeout", val);
+ parsed = 1;
+ /*
+ * PRAGMA replication_priority; -- DB_ENV->rep_get_priority
+ * PRAGMA replication_priority = N; -- DB_ENV->rep_set_priority
+ * N provides the 1 or greater priority of the replication site,
+ * which is used to decide what site is elected master.
+ */
+ } else if (sqlite3StrNICmp(zLeft, "replication_priority", 20) == 0) {
+ int iLimit = -2;
+ u_int32_t val;
+
+ if (zRight) {
+ if (!sqlite3GetInt32(zRight, &iLimit) || iLimit < 1)
+ sqlite3ErrorMsg(pParse,
+ "Invalid value replication_priority %s",
+ zRight);
+ else {
+ val = iLimit;
+ if ((ret =
+ pDb->pBt->pBt->dbenv->rep_set_priority(
+ pDb->pBt->pBt->dbenv, val)) != 0) {
+ sqlite3ErrorMsg(pParse,
+ "Failed to set replication "
+ "priority. Error: %d.", ret);
+ }
+ }
+ }
+
+ /* Get existing value */
+ pDb->pBt->pBt->dbenv->rep_get_priority(
+ pDb->pBt->pBt->dbenv, &val);
+
+ returnSingleInt(pParse, "replication_priority", val);
+ parsed = 1;
+ /*
+ * PRAGMA replication_num_sites; -- DB_ENV->repmgr_site_list
+ * Returns the number of sites in the replication group.
+ */
+ } else if (sqlite3StrNICmp(zLeft, "replication_num_sites", 21) == 0) {
+ u_int32_t val = -1;
+ DB_REPMGR_SITE *sites = NULL;
+
+ /* Get existing value */
+ pDb->pBt->pBt->dbenv->repmgr_site_list(
+ pDb->pBt->pBt->dbenv, &val, &sites);
+
+ /*
+ * repmgr_site_list only gets the list of remote sites,
+ * so add one for the local site if replication has been
+ * started.
+ */
+ if (pDb->pBt->pBt->repStarted)
+ val++;
+
+ if (sites)
+ sqlite3_free(sites);
+
+ returnSingleInt(pParse, "replication_num_sites", val);
+ parsed = 1;
+ /*
+ * PRAGMA replication_perm_failed; -- DB_REP_PERM_FAILED
+ * Returns the number of times since the last time this pragma was
+ * called the master was unable to get enough acknowledgments from
+ * clients when committing a transaction.
+ */
+ } else if (sqlite3StrNICmp(zLeft, "replication_perm_failed", 23) == 0) {
+ u_int32_t val = 0;
+
+ sqlite3_mutex_enter(pDb->pBt->pBt->mutex);
+ val = pDb->pBt->pBt->permFailures;
+ pDb->pBt->pBt->permFailures = 0;
+ sqlite3_mutex_leave(pDb->pBt->pBt->mutex);
+
+ returnSingleInt(pParse, "replication_perm_failed", val);
+ parsed = 1;
+ /*
+ * PRAGMA replication_site_status;
+ * Returns MASTER if the site is a replication master, CLIENT if the
+ * site is a replication client, and UNKNOWN if replication is not
+ * enabled, or the site status is still being decided (such as during
+ * an election).
+ */
+ } else if (sqlite3StrNICmp(zLeft, "replication_site_status", 23) == 0) {
+ rep_site_type_t val;
+ const char *type;
+
+ sqlite3_mutex_enter(pDb->pBt->pBt->mutex);
+ val = pDb->pBt->pBt->repRole;
+ sqlite3_mutex_leave(pDb->pBt->pBt->mutex);
+
+ type = repSiteTypeToText(val);
+ sqlite3VdbeSetNumCols(pParse->pVdbe, 1);
+ sqlite3VdbeSetColName(pParse->pVdbe, 0, COLNAME_NAME,
+ zLeft, SQLITE_STATIC);
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_String8, 0, 1, 0, type, 0);
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_ResultRow, 1, 1);
+ parsed = 1;
+ /*
+ * PRAGMA replication_get_master;
+ * Returns the host:port of the master, or NULL if replication has
+ * not started, or there is none.
+ */
+ } else if (sqlite3StrNICmp(zLeft, "replication_get_master", 23) == 0) {
+ const char *address;
+
+ sqlite3_mutex_enter(pDb->pBt->pBt->mutex);
+ address = pDb->pBt->pBt->master_address;
+ sqlite3_mutex_leave(pDb->pBt->pBt->mutex);
+
+ sqlite3VdbeSetNumCols(pParse->pVdbe, 1);
+ sqlite3VdbeSetColName(pParse->pVdbe, 0, COLNAME_NAME,
+ zLeft, SQLITE_STATIC);
+ if (address)
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_String8, 0, 1, 0, address, 0);
+ else
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_String8, 0, 1, 0, "NULL", 0);
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_ResultRow, 1, 1);
+ parsed = 1;
}
/* Return semantics to match strcmp. */