Skip to content

Commit 0156489

Browse files
committed
PHPC-353: Support RP and WC in Manager options array
The read preference and write concern are complex structures, so we can't simply set their options on the URI as we do for other things (e.g. auth credentials).
1 parent 490e565 commit 0156489

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed

php_phongo.c

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,18 @@ mongoc_uri_t *php_phongo_make_uri(const char *uri_string, bson_t *options TSRMLS
15121512
while (bson_iter_next (&iter)) {
15131513
const char *key = bson_iter_key(&iter);
15141514

1515+
/* Skip read preference and write concern options, as those must be
1516+
* processed after the mongoc_client_t is constructed. */
1517+
if (!strcasecmp(key, "journal") ||
1518+
!strcasecmp(key, "readpreference") ||
1519+
!strcasecmp(key, "readpreferencetags") ||
1520+
!strcasecmp(key, "safe") ||
1521+
!strcasecmp(key, "slaveok") ||
1522+
!strcasecmp(key, "w") ||
1523+
!strcasecmp(key, "wtimeoutms")) {
1524+
continue;
1525+
}
1526+
15151527
if (mongoc_uri_option_is_bool(key)) {
15161528
mongoc_uri_set_option_as_bool (uri, key, bson_iter_as_bool(&iter));
15171529
}
@@ -1595,6 +1607,176 @@ void php_phongo_populate_default_ssl_ctx(php_stream_context *ctx, zval *driverOp
15951607
#undef SET_STRING_CTX
15961608
} /* }}} */
15971609

1610+
bool php_phongo_apply_rp_options_to_client(mongoc_client_t *client, bson_t *options TSRMLS_DC) /* {{{ */
1611+
{
1612+
bson_iter_t iter;
1613+
mongoc_read_prefs_t *new_rp;
1614+
const mongoc_read_prefs_t *old_rp;
1615+
1616+
if (!(old_rp = mongoc_client_get_read_prefs(client))) {
1617+
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "Client does not have a read preference");
1618+
1619+
return false;
1620+
}
1621+
1622+
new_rp = mongoc_read_prefs_copy(old_rp);
1623+
1624+
if (bson_iter_init_find_case(&iter, options, "slaveok") && BSON_ITER_HOLDS_BOOL(&iter)) {
1625+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY_PREFERRED);
1626+
}
1627+
1628+
if (bson_iter_init_find_case(&iter, options, "readpreference") && BSON_ITER_HOLDS_UTF8(&iter)) {
1629+
const char *str = bson_iter_utf8(&iter, NULL);
1630+
1631+
if (0 == strcasecmp("primary", str)) {
1632+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_PRIMARY);
1633+
} else if (0 == strcasecmp("primarypreferred", str)) {
1634+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_PRIMARY_PREFERRED);
1635+
} else if (0 == strcasecmp("secondary", str)) {
1636+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY);
1637+
} else if (0 == strcasecmp("secondarypreferred", str)) {
1638+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY_PREFERRED);
1639+
} else if (0 == strcasecmp("nearest", str)) {
1640+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_NEAREST);
1641+
} else {
1642+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Unsupported readPreference value: '%s'", str);
1643+
mongoc_read_prefs_destroy(new_rp);
1644+
1645+
return false;
1646+
}
1647+
}
1648+
1649+
if (bson_iter_init_find_case(&iter, options, "readpreferencetags") && BSON_ITER_HOLDS_ARRAY(&iter)) {
1650+
bson_t tags;
1651+
uint32_t len;
1652+
const uint8_t *data;
1653+
1654+
bson_iter_array(&iter, &len, &data);
1655+
1656+
if (bson_init_static(&tags, data, len)) {
1657+
mongoc_read_prefs_set_tags(new_rp, &tags);
1658+
}
1659+
}
1660+
1661+
if (mongoc_read_prefs_get_mode(new_rp) == MONGOC_READ_PRIMARY &&
1662+
!bson_empty(mongoc_read_prefs_get_tags(new_rp))) {
1663+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Primary read preference mode conflicts with tags");
1664+
mongoc_read_prefs_destroy(new_rp);
1665+
1666+
return false;
1667+
}
1668+
1669+
/* This may be redundant in light of the last check (primary with tags), but
1670+
* we'll check anyway in case additional validation is implemented. */
1671+
if (!mongoc_read_prefs_is_valid(new_rp)) {
1672+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Read preference is not valid");
1673+
mongoc_read_prefs_destroy(new_rp);
1674+
1675+
return false;
1676+
}
1677+
1678+
mongoc_client_set_read_prefs(client, new_rp);
1679+
mongoc_read_prefs_destroy(new_rp);
1680+
1681+
return true;
1682+
} /* }}} */
1683+
1684+
bool php_phongo_apply_wc_options_to_client(mongoc_client_t *client, bson_t *options TSRMLS_DC) /* {{{ */
1685+
{
1686+
bson_iter_t iter;
1687+
int32_t wtimeoutms;
1688+
mongoc_write_concern_t *new_wc;
1689+
const mongoc_write_concern_t *old_wc;
1690+
1691+
if (!(old_wc = mongoc_client_get_write_concern(client))) {
1692+
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "Client does not have a write concern");
1693+
1694+
return false;
1695+
}
1696+
1697+
wtimeoutms = mongoc_write_concern_get_wtimeout(old_wc);
1698+
1699+
new_wc = mongoc_write_concern_copy(old_wc);
1700+
1701+
if (bson_iter_init_find_case(&iter, options, "safe") && BSON_ITER_HOLDS_BOOL(&iter)) {
1702+
mongoc_write_concern_set_w(new_wc, bson_iter_bool(&iter) ? 1 : MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED);
1703+
}
1704+
1705+
if (bson_iter_init_find_case(&iter, options, "wtimeoutms") && BSON_ITER_HOLDS_INT32(&iter)) {
1706+
wtimeoutms = bson_iter_int32(&iter);
1707+
}
1708+
1709+
if (bson_iter_init_find_case(&iter, options, "journal") && BSON_ITER_HOLDS_BOOL(&iter)) {
1710+
mongoc_write_concern_set_journal(new_wc, bson_iter_bool(&iter));
1711+
}
1712+
1713+
if (bson_iter_init_find_case(&iter, options, "w")) {
1714+
if (BSON_ITER_HOLDS_INT32(&iter)) {
1715+
int32_t value = bson_iter_int32(&iter);
1716+
1717+
switch (value) {
1718+
case MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED:
1719+
case MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED:
1720+
mongoc_write_concern_set_w(new_wc, value);
1721+
break;
1722+
1723+
default:
1724+
if (value > 0) {
1725+
mongoc_write_concern_set_w(new_wc, value);
1726+
break;
1727+
}
1728+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Unsupported w value: %d", value);
1729+
mongoc_write_concern_destroy(new_wc);
1730+
1731+
return false;
1732+
}
1733+
} else if (BSON_ITER_HOLDS_UTF8(&iter)) {
1734+
const char *str = bson_iter_utf8(&iter, NULL);
1735+
1736+
if (0 == strcasecmp("majority", str)) {
1737+
mongoc_write_concern_set_wmajority(new_wc, wtimeoutms);
1738+
} else {
1739+
mongoc_write_concern_set_wtag(new_wc, str);
1740+
}
1741+
}
1742+
}
1743+
1744+
/* Only set wtimeout if it's still applicable; otherwise, clear it. */
1745+
if (mongoc_write_concern_get_w(new_wc) > 1 ||
1746+
mongoc_write_concern_get_wmajority(new_wc) ||
1747+
mongoc_write_concern_get_wtag(new_wc)) {
1748+
mongoc_write_concern_set_wtimeout(new_wc, wtimeoutms);
1749+
} else {
1750+
mongoc_write_concern_set_wtimeout(new_wc, 0);
1751+
}
1752+
1753+
if (mongoc_write_concern_get_journal(new_wc)) {
1754+
int32_t w = mongoc_write_concern_get_w(new_wc);
1755+
1756+
if (w == MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED || w == MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED) {
1757+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Journal conflicts with w value: %d", w);
1758+
mongoc_write_concern_destroy(new_wc);
1759+
1760+
return false;
1761+
}
1762+
}
1763+
1764+
/* This may be redundant in light of the last check (unacknowledged w with
1765+
journal), but we'll check anyway in case additional validation is
1766+
implemented. */
1767+
if (!_mongoc_write_concern_is_valid(new_wc)) {
1768+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Write concern is not valid");
1769+
mongoc_write_concern_destroy(new_wc);
1770+
1771+
return false;
1772+
}
1773+
1774+
mongoc_client_set_write_concern(client, new_wc);
1775+
mongoc_write_concern_destroy(new_wc);
1776+
1777+
return true;
1778+
} /* }}} */
1779+
15981780
mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, zval *driverOptions TSRMLS_DC) /* {{{ */
15991781
{
16001782
zval **tmp;

php_phongo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ void php_phongo_read_preference_to_zval(zval *retval, const mongoc_read_prefs_t
130130
void php_phongo_write_concern_to_zval(zval *retval, const mongoc_write_concern_t *write_concern);
131131
void php_phongo_cursor_to_zval(zval *retval, php_phongo_cursor_t *cursor);
132132

133+
bool php_phongo_apply_rp_options_to_client(mongoc_client_t *client, bson_t *options TSRMLS_DC);
134+
bool php_phongo_apply_wc_options_to_client(mongoc_client_t *client, bson_t *options TSRMLS_DC);
133135
mongoc_uri_t *php_phongo_make_uri(const char *uri_string, bson_t *options TSRMLS_DC);
134136
mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, zval *driverOptions TSRMLS_DC);
135137
void php_phongo_objectid_new_from_oid(zval *object, const bson_oid_t *oid TSRMLS_DC);

src/MongoDB/Manager.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ PHP_METHOD(Manager, __construct)
9898
return;
9999
}
100100

101+
if (!php_phongo_apply_rp_options_to_client(intern->client, &bson_options TSRMLS_CC) ||
102+
!php_phongo_apply_wc_options_to_client(intern->client, &bson_options TSRMLS_CC)) {
103+
/* Exception should already have been thrown */
104+
bson_destroy(&bson_options);
105+
106+
return;
107+
}
108+
101109
bson_destroy(&bson_options);
102110
}
103111
/* }}} */

0 commit comments

Comments
 (0)