Skip to content
Browse files

Initial import

  • Loading branch information...
0 parents commit edb48701b5e53a9fdeff26c6025a5a34b4608a29 Mikko Koppanen committed
2 ChangeLog
@@ -0,0 +1,2 @@
+0.1.0
+- Initial release
174 LICENSE
@@ -0,0 +1,174 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
140 README.md
@@ -0,0 +1,140 @@
+# Introduction
+
+Pretty much very experimental PDO driver for Cassandra CQL.
+
+
+# Dependencies
+
+ - PHP and PDO
+ - Thrift
+ - Boost (shared_ptr)
+
+
+# How to build?
+
+First install thrift from http://thrift.apache.org/download/. Thrift should depend on
+boost shared_ptr so while installing thrift you are installing rest of the dependencies
+for pdo_cassandra (apart from PHP and PDO of course).
+
+pdo_cassandra ./configure script takes the following options:
+
+ - --with-pdo-cassandra[=FILE] where file is optional path to Cassandra Thrift definitions file. The file is
+ bundled with the package so it is unlikely that alternative file is needed unless testing new versions.
+
+ - --with-thrift-dir[=DIR] can be used to specify 'non-standard' thrift installation prefix.
+
+ - --with-boost-dir[=DIR] can be used to specify 'non-standard' boost installation prefix.
+
+
+# Running tests
+
+After a successful build tests can be executed using an instance of Cassandra. Default config
+for Cassandra should be fine.
+
+Before make test:
+
+ $ cp tests/config.inc-dist tests/config.inc
+ $ $EDITOR tests/config.inc
+
+This is to prevent accidentally dropping keyspaces that might in use.
+
+
+# DSN
+
+The DSN format is as follows:
+
+ - "cassandra:host=127.0.0.1;port=9160"
+
+Multiple hosts can be specified using the following format:
+
+ - "cassandra:host=127.0.0.1;port=9160,host=localhost;port=9160"
+
+
+# Prepared statements
+
+CQL doesn't support prepared statements so PDO emulation layer is used for this. Notice that quoting of the
+identifiers is simple 'addslashes' call on the data and isn't necessarily as safe as prepared statements.
+
+It is also important to specify the types of the bound parameters as (string) "1" might get convert to a
+string value internally even though integer might be more appropriated.
+
+
+# Transactions
+
+Transactions are not supported and calling PDO::beginTransaction will result in an exception.
+
+
+# Contributing
+
+Pull requests containing fixes and/or additional tests are highly appreciated.
+
+
+# Driver specific attributes for PDO::setAttribute
+
+<table>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_NUM_RETRIES</td>
+ <td>integer</td>
+ <td>The amount of connection retries</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_RETRY_INTERVAL</td>
+ <td>integer</td>
+ <td>Sets how many times to keep retrying a host before marking it as down.</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_MAX_CONSECUTIVE_FAILURES</td>
+ <td>integer</td>
+ <td>Sets how many times to keep retrying a host before marking it as down.</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_LINGER</td>
+ <td>integer</td>
+ <td>How long does the socket linger after it's being closed</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_NO_DELAY</td>
+ <td>boolean</td>
+ <td>Whether to enable/disable Nagle algorithm</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_CONN_TIMEOUT</td>
+ <td>integer</td>
+ <td>Connection timeout</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_RECV_TIMEOUT</td>
+ <td>integer</td>
+ <td>Receive timeout</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_SEND_TIMEOUT</td>
+ <td>integer</td>
+ <td>Send timeout</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_COMPRESSION</td>
+ <td>boolean</td>
+ <td>Whether to enable/disable compression</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_THRIFT_DEBUG</td>
+ <td>boolean</td>
+ <td>Converts thrift debug output into PHP warnings</td>
+ </tr>
+</table>
+
+## The constructor honours the following options
+
+These options can be passed in the fourth argument for PDO constructor
+
+<table>
+ <tr>
+ <td>PDO_ATTR_TIMEOUT</td>
+ <td>Connection timeout value</td>
+ </tr>
+ <tr>
+ <td>PDO::CASSANDRA_ATTR_THRIFT_DEBUG</td>
+ <td>Converts thrift debug output into PHP warnings</td>
+ </tr>
+</table>
609 cassandra_driver.cpp
@@ -0,0 +1,609 @@
+/*
+ * Copyright 2011 DataStax
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "php_pdo_cassandra.hpp"
+#include "php_pdo_cassandra_int.hpp"
+
+/* Defined in pdo_cassandra_statement.cpp */
+extern struct pdo_stmt_methods cassandra_stmt_methods;
+
+/* {{{ pdo_cassandra_functions[] */
+const zend_function_entry pdo_cassandra_functions[] = {
+ {NULL, NULL, NULL}
+};
+/* }}} */
+
+/* Declarations */
+static int pdo_cassandra_handle_close(pdo_dbh_t *dbh TSRMLS_DC);
+static int pdo_cassandra_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC);
+static int pdo_cassandra_handle_prepare(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC);
+static int pdo_cassandra_handle_quote(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC);
+static long pdo_cassandra_handle_execute(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC);
+static int pdo_cassandra_handle_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC);
+static int pdo_cassandra_handle_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC);
+static int pdo_cassandra_get_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC);
+
+static struct pdo_dbh_methods cassandra_methods = {
+ pdo_cassandra_handle_close,
+ pdo_cassandra_handle_prepare,
+ pdo_cassandra_handle_execute,
+ pdo_cassandra_handle_quote,
+ NULL,
+ NULL,
+ NULL,
+ pdo_cassandra_handle_set_attribute,
+ NULL,
+ pdo_cassandra_get_error,
+ pdo_cassandra_handle_get_attribute,
+ NULL,
+ NULL,
+ NULL
+};
+
+/** {{{ static int pdo_cassandra_get_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC)
+*/
+static int pdo_cassandra_get_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC)
+{
+ pdo_cassandra_db_handle *H = static_cast<pdo_cassandra_db_handle *> (dbh->driver_data);
+ pdo_cassandra_einfo *einfo = &H->einfo;
+
+ if (einfo->errmsg) {
+ add_next_index_long(info, einfo->errcode);
+ add_next_index_string(info, einfo->errmsg, 1);
+ }
+ return 1;
+}
+/* }}} */
+
+/** {{{ static void pdo_cassandra_set_cqlstate(pdo_error_type buffer, pdo_cassandra_error code)
+*/
+static void pdo_cassandra_set_cqlstate(pdo_error_type buffer, pdo_cassandra_error code)
+{
+ switch (code) {
+ case PDO_CASSANDRA_TRANSPORT_ERROR:
+ strlcpy(buffer, "08006", sizeof(pdo_error_type));
+ break;
+
+ default:
+ strlcpy(buffer, "HY000", sizeof(pdo_error_type));
+ break;
+ }
+}
+/* }}} */
+
+/** {{{ void pdo_cassandra_error_ex(pdo_dbh_t *dbh TSRMLS_DC, pdo_cassandra_error code, const char *file, int line, const char *message, ...)
+*/
+void pdo_cassandra_error_ex(pdo_dbh_t *dbh TSRMLS_DC, pdo_cassandra_error code, const char *file, int line, zend_bool force_exception, const char *message, ...)
+{
+ pdo_cassandra_db_handle *H = static_cast<pdo_cassandra_db_handle *> (dbh->driver_data);
+ pdo_cassandra_einfo *einfo = &H->einfo;
+
+ va_list args;
+ char buffer[256];
+ size_t buffer_len;
+
+ if (einfo->errmsg) {
+ /* Free previous error message */
+ pefree(einfo->errmsg, dbh->is_persistent);
+ einfo->errmsg = NULL;
+ }
+
+ einfo->errcode = code;
+ einfo->file = file;
+ einfo->line = line;
+
+ /* Set the correct CQLSTATE */
+ pdo_cassandra_set_cqlstate(dbh->error_code, code);
+
+ va_start(args, message);
+ buffer_len = vsnprintf(buffer, sizeof(buffer), message, args);
+ va_end(args);
+
+ einfo->errmsg = (char *) pemalloc(buffer_len + 1, dbh->is_persistent);
+ memcpy(einfo->errmsg, buffer, buffer_len + 1);
+
+ if (dbh->error_mode == PDO_ERRMODE_EXCEPTION || force_exception) {
+ zend_throw_exception_ex(php_pdo_get_exception(), code TSRMLS_CC, "CQLSTATE[%s] [%d] %s", dbh->error_code, code, buffer);
+ } else if (dbh->error_mode == PDO_ERRMODE_WARNING) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "CQLSTATE[%s] [%d] %s", dbh->error_code, code, buffer);
+ }
+}
+/* }}} */
+
+/** {{{ static zend_bool parse_dsn(pdo_dbh_t *dbh, pdo_cassandra_db_handle *H, const char *dsn TSRMLS_DC)
+*/
+static zend_bool parse_dsn(pdo_dbh_t *dbh, pdo_cassandra_db_handle *H, const char *dsn TSRMLS_DC)
+{
+ char *ptr = NULL, *pch = NULL, *last = NULL;
+
+ ptr = estrdup(dsn);
+ pch = php_strtok_r(ptr, ",", &last);
+
+ while (pch != NULL) {
+ char *host = NULL;
+ int port = 0;
+
+ struct pdo_data_src_parser vars[] = {
+ { "host", "", 0 },
+ { "port", "", 0 },
+ };
+
+ if (php_pdo_parse_data_source(pch, strlen(pch), vars, 2) != 2) {
+ efree(ptr);
+ return 0;
+ }
+
+ host = vars[0].optval;
+ port = atoi(vars[1].optval);
+#if 0
+ std::cerr << "Adding host=[" << host << "] port=[" << port << "]" << std::endl;
+#endif
+ try {
+ H->socket->addServer (host, port);
+ } catch (std::exception &re) {
+ efree(ptr);
+ return 0;
+ }
+
+ pch = php_strtok_r(NULL, ",", &last);
+
+ for (size_t i = 0; i < sizeof(vars) / sizeof(vars[0]); i++) {
+ if (vars[i].freeme) {
+ efree(vars[i].optval);
+ }
+ }
+ }
+ efree(ptr);
+ return 1;
+}
+/* }}} */
+
+/* {{{ static void php_cassandra_thrift_debug_output (const char *s)
+*/
+static void php_cassandra_thrift_debug_output (const char *s)
+{
+ TSRMLS_FETCH ();
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "PDO Cassandra thrift debug: %s", s);
+}
+/* }}} */
+
+/* {{{ static void php_cassandra_thrift_no_output (const char *)
+*/
+static void php_cassandra_thrift_no_output (const char *)
+{}
+/* }}} */
+
+/** {{{ static void pdo_cassandra_toggle_thrift_debug(zend_bool enabled)
+*/
+static void pdo_cassandra_toggle_thrift_debug(zend_bool enabled)
+{
+ if (enabled) {
+ // Convert thift messages to php warnings
+ GlobalOutput.setOutputFunction(&php_cassandra_thrift_debug_output);
+ } else {
+ // Disable output from thrift library
+ GlobalOutput.setOutputFunction(&php_cassandra_thrift_no_output);
+ }
+}
+/* }}} */
+
+/** {{{ static void php_cassandra_handle_auth(pdo_dbh_t *dbh, pdo_cassandra_db_handle *H)
+ TODO: this needs to be actually tested with a setup where logins work
+*/
+static void php_cassandra_handle_auth(pdo_dbh_t *dbh, pdo_cassandra_db_handle *H)
+{
+ if (dbh->username && strlen(dbh->username) && dbh->password && strlen(dbh->password)) {
+ std::string user = dbh->username;
+ std::string pass = dbh->password;
+
+ std::map<std::string, std::string> auth_value;
+ auth_value.insert(std::pair<std::string, std::string>(user, pass));
+
+ AuthenticationRequest auth_request;
+ auth_request.credentials = auth_value;
+ H->client->login(auth_request);
+ }
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC)
+*/
+static int pdo_cassandra_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC)
+{
+ pdo_cassandra_db_handle *H = new pdo_cassandra_db_handle;
+
+ dbh->driver_data = NULL;
+ dbh->methods = &cassandra_methods;
+ H->compression = 0;
+ H->einfo.errcode = 0;
+ H->einfo.errmsg = NULL;
+
+ H->socket.reset(new TSocketPool);
+ H->transport.reset(new TFramedTransport(H->socket));
+ H->protocol.reset(new TBinaryProtocol(H->transport));
+ H->client.reset(new CassandraClient(H->protocol));
+ dbh->driver_data = H;
+
+ /* set possible connection timeout */
+ long timeout = 0;
+
+ if (driver_options) {
+ timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout TSRMLS_CC);
+ H->socket->setConnTimeout(timeout);
+
+ if (pdo_attr_lval(driver_options, static_cast <pdo_attribute_type>(PDO_CASSANDRA_ATTR_THRIFT_DEBUG), 0 TSRMLS_CC)) {
+ // Convert thift messages to php warnings
+ pdo_cassandra_toggle_thrift_debug(1);
+ } else {
+ // Disable output from thrift library
+ pdo_cassandra_toggle_thrift_debug(0);
+ }
+ }
+
+ /* Break down the values */
+ zend_bool rc = 0;
+ if (dbh->data_source_len > 0) {
+ rc = parse_dsn(dbh, H, dbh->data_source TSRMLS_CC);
+ }
+
+ if (!rc) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_INVALID_CONNECTION_STRING, "%s", "Invalid connection string attribute");
+ pdo_cassandra_handle_close(dbh TSRMLS_CC);
+ return 0;
+ }
+
+ try {
+ H->transport->open();
+ php_cassandra_handle_auth (dbh, H);
+ return 1;
+ } catch (NotFoundException &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_NOT_FOUND, "%s", e.what());
+ } catch (InvalidRequestException &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_INVALID_REQUEST, "%s", e.why.c_str());
+ } catch (UnavailableException &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_UNAVAILABLE, "%s", e.what());
+ } catch (TimedOutException &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_TIMED_OUT, "%s", e.what());
+ } catch (AuthenticationException &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_AUTHENTICATION_ERROR, "%s", e.why.c_str());
+ } catch (AuthorizationException &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_AUTHORIZATION_ERROR, "%s", e.why.c_str());
+ } catch (SchemaDisagreementException &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_SCHEMA_DISAGREEMENT, "%s", e.what());
+ } catch (TTransportException &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_TRANSPORT_ERROR, "%s", e.what());
+ } catch (TException &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_GENERAL_ERROR, "%s", e.what());
+ } catch (std::exception &e) {
+ pdo_cassandra_error_exception(dbh, PDO_CASSANDRA_GENERAL_ERROR, "%s", e.what());
+ }
+ return 0;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_handle_prepare(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
+*/
+static int pdo_cassandra_handle_prepare(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
+{
+ pdo_cassandra_db_handle *H = static_cast<pdo_cassandra_db_handle *> (dbh->driver_data);
+ pdo_cassandra_stmt *S = new pdo_cassandra_stmt;
+
+ /* transfer handle with the statement */
+ S->H = H;
+ S->result.reset ();
+
+ /* Fill in necessary driver data */
+ stmt->driver_data = S;
+ stmt->methods = &cassandra_stmt_methods;
+ stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
+
+ return 1;
+}
+/* }}} */
+
+/** {{{ static long pdo_cassandra_handle_execute(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
+*/
+static long pdo_cassandra_handle_execute(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
+{
+ pdo_cassandra_db_handle *H = static_cast<pdo_cassandra_db_handle *> (dbh->driver_data);
+
+ try {
+ /* Verify and try to reconnect if necessary */
+ if (!H->transport->isOpen()) {
+ H->transport->open();
+ }
+
+ std::string q(sql);
+ CqlResult result;
+
+ H->client->execute_cql_query(result, q, (H->compression ? Compression::GZIP : Compression::NONE));
+
+ if (result.type == CqlResultType::INT) {
+ return result.num;
+ }
+ return 0;
+ } catch (NotFoundException &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_NOT_FOUND, "%s", e.what());
+ } catch (InvalidRequestException &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_INVALID_REQUEST, "%s", e.why.c_str());
+ } catch (UnavailableException &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_UNAVAILABLE, "%s", e.what());
+ } catch (TimedOutException &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_TIMED_OUT, "%s", e.what());
+ } catch (AuthenticationException &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_AUTHENTICATION_ERROR, "%s", e.why.c_str());
+ } catch (AuthorizationException &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_AUTHORIZATION_ERROR, "%s", e.why.c_str());
+ } catch (SchemaDisagreementException &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_SCHEMA_DISAGREEMENT, "%s", e.what());
+ } catch (TTransportException &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_TRANSPORT_ERROR, "%s", e.what());
+ } catch (TException &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_GENERAL_ERROR, "%s", e.what());
+ } catch (std::exception &e) {
+ pdo_cassandra_error(dbh, PDO_CASSANDRA_GENERAL_ERROR, "%s", e.what());
+ }
+ return 0;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_handle_quote(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC)
+*/
+static int pdo_cassandra_handle_quote(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC)
+{
+ char *escaped;
+ int new_length;
+
+ // const_cast should be fine here, php_addslashes shouldn't modify the data
+ escaped = php_addslashes(const_cast <char *>(unquoted), unquotedlen, &new_length, 0 TSRMLS_CC);
+
+ if (!escaped) {
+ return 0;
+ }
+
+ *quotedlen = spprintf(quoted, 0, "'%s'", escaped);
+ efree (escaped);
+ return 1;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_handle_close(pdo_dbh_t *dbh TSRMLS_DC)
+*/
+static int pdo_cassandra_handle_close(pdo_dbh_t *dbh TSRMLS_DC)
+{
+ pdo_cassandra_db_handle *H = static_cast <pdo_cassandra_db_handle *>(dbh->driver_data);
+
+ if (H) {
+ pdo_cassandra_einfo *einfo = &H->einfo;
+
+ H->transport->close();
+ H->socket.reset();
+ H->transport.reset();
+ H->protocol.reset();
+ H->client.reset();
+
+ if (einfo->errmsg) {
+ pefree(einfo->errmsg, dbh->is_persistent);
+ einfo->errmsg = NULL;
+ }
+ delete H;
+ dbh->driver_data = NULL;
+ }
+ return 0;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_handle_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
+*/
+static int pdo_cassandra_handle_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
+{
+ pdo_cassandra_db_handle *H = static_cast <pdo_cassandra_db_handle *>(dbh->driver_data);
+ pdo_cassandra_constant attribute = static_cast <pdo_cassandra_constant>(attr);
+
+ switch (attribute) {
+
+ case PDO_CASSANDRA_ATTR_NUM_RETRIES:
+ convert_to_long(val);
+ H->socket->setNumRetries(Z_LVAL_P(val));
+ break;
+
+ case PDO_CASSANDRA_ATTR_RETRY_INTERVAL:
+ convert_to_long(val);
+ H->socket->setRetryInterval(Z_LVAL_P(val));
+ break;
+
+ case PDO_CASSANDRA_ATTR_MAX_CONSECUTIVE_FAILURES:
+ convert_to_long(val);
+ H->socket->setMaxConsecutiveFailures(Z_LVAL_P(val));
+ break;
+
+ case PDO_CASSANDRA_ATTR_RANDOMIZE:
+ convert_to_boolean(val);
+ H->socket->setMaxConsecutiveFailures(Z_BVAL_P(val));
+ break;
+
+ case PDO_CASSANDRA_ATTR_ALWAYS_TRY_LAST:
+ convert_to_boolean(val);
+ H->socket->setAlwaysTryLast(Z_BVAL_P(val));
+ break;
+
+ case PDO_CASSANDRA_ATTR_LINGER:
+ convert_to_long(val);
+
+ if (Z_LVAL_P(val) == 0) {
+ H->socket->setLinger(false, 0);
+ } else {
+ H->socket->setLinger(true, Z_LVAL_P(val));
+ }
+ break;
+
+ case PDO_CASSANDRA_ATTR_NO_DELAY:
+ convert_to_boolean(val);
+ H->socket->setNoDelay(Z_BVAL_P(val));
+ break;
+
+ case PDO_CASSANDRA_ATTR_CONN_TIMEOUT:
+ convert_to_long(val);
+ H->socket->setConnTimeout(Z_LVAL_P(val));
+ break;
+
+ case PDO_CASSANDRA_ATTR_RECV_TIMEOUT:
+ convert_to_long(val);
+ H->socket->setRecvTimeout(Z_LVAL_P(val));
+ break;
+
+ case PDO_CASSANDRA_ATTR_SEND_TIMEOUT:
+ convert_to_long(val);
+ H->socket->setSendTimeout(Z_LVAL_P(val));
+ break;
+
+ case PDO_CASSANDRA_ATTR_COMPRESSION:
+ convert_to_boolean(val);
+ H->compression = Z_BVAL_P(val);
+ break;
+
+ case PDO_CASSANDRA_ATTR_THRIFT_DEBUG:
+ convert_to_boolean(val);
+ if (Z_BVAL_P(val)) {
+ // Convert thift messages to php warnings
+ GlobalOutput.setOutputFunction(&php_cassandra_thrift_debug_output);
+ } else {
+ // Disable output from thrift library
+ GlobalOutput.setOutputFunction(&php_cassandra_thrift_no_output);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_handle_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC)
+*/
+static int pdo_cassandra_handle_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC)
+{
+ pdo_cassandra_db_handle *H = static_cast <pdo_cassandra_db_handle *>(dbh->driver_data);
+
+ switch (attr) {
+
+ case PDO_ATTR_SERVER_VERSION:
+ {
+ std::string version;
+ H->client->describe_version(version);
+ ZVAL_STRING(return_value, version.c_str(), 1);
+ }
+ break;
+
+ case PDO_ATTR_CLIENT_VERSION:
+ ZVAL_STRING(return_value, PHP_PDO_CASSANDRA_EXTVER, 1);
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+pdo_driver_t pdo_cassandra_driver = {
+ PDO_DRIVER_HEADER(cassandra),
+ pdo_cassandra_handle_factory
+};
+
+/* {{{ PHP_MINIT_FUNCTION */
+PHP_MINIT_FUNCTION(pdo_cassandra)
+{
+ // Disable debug output from Thrift
+ pdo_cassandra_toggle_thrift_debug(0);
+
+ // Class constants
+#define PHP_PDO_CASSANDRA_REGISTER_CONST_LONG(const_name, value) \
+ zend_declare_class_constant_long(php_pdo_get_dbh_ce(), const_name, sizeof(const_name)-1, (long)value TSRMLS_CC);
+
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_NUM_RETRIES", PDO_CASSANDRA_ATTR_NUM_RETRIES);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_RETRY_INTERVAL", PDO_CASSANDRA_ATTR_RETRY_INTERVAL);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_MAX_CONSECUTIVE_FAILURES", PDO_CASSANDRA_ATTR_MAX_CONSECUTIVE_FAILURES);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_RANDOMIZE", PDO_CASSANDRA_ATTR_RANDOMIZE);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_ALWAYS_TRY_LAST", PDO_CASSANDRA_ATTR_ALWAYS_TRY_LAST);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_LINGER", PDO_CASSANDRA_ATTR_LINGER);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_NO_DELAY", PDO_CASSANDRA_ATTR_NO_DELAY);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_CONN_TIMEOUT", PDO_CASSANDRA_ATTR_CONN_TIMEOUT);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_RECV_TIMEOUT", PDO_CASSANDRA_ATTR_RECV_TIMEOUT);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_SEND_TIMEOUT", PDO_CASSANDRA_ATTR_SEND_TIMEOUT);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_COMPRESSION", PDO_CASSANDRA_ATTR_COMPRESSION);
+ PHP_PDO_CASSANDRA_REGISTER_CONST_LONG("CASSANDRA_ATTR_THRIFT_DEBUG", PDO_CASSANDRA_ATTR_THRIFT_DEBUG);
+
+#undef PHP_PDO_CASSANDRA_REGISTER_CONST_LONG
+
+ return php_pdo_register_driver(&pdo_cassandra_driver);
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION */
+PHP_MSHUTDOWN_FUNCTION(pdo_cassandra)
+{
+ php_pdo_unregister_driver(&pdo_cassandra_driver);
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(pdo_cassandra)
+{
+ php_info_print_table_start();
+ php_info_print_table_header(2, "PDO Driver for Cassandra", "enabled");
+ php_info_print_table_header(2, "PDO Driver for Cassandra version", PHP_PDO_CASSANDRA_EXTVER);
+ php_info_print_table_end();
+}
+/* }}} */
+
+/* {{{ pdo_cassandra_deps
+ */
+#if ZEND_MODULE_API_NO >= 20050922
+static const zend_module_dep pdo_cassandra_deps[] = {
+ ZEND_MOD_REQUIRED("pdo")
+ {NULL, NULL, NULL}
+};
+#endif
+/* }}} */
+
+/* {{{ pdo_cassandra_module_entry
+ */
+zend_module_entry pdo_cassandra_module_entry = {
+#if ZEND_MODULE_API_NO >= 20050922
+ STANDARD_MODULE_HEADER_EX, NULL,
+ pdo_cassandra_deps,
+#else
+ STANDARD_MODULE_HEADER,
+#endif
+ PHP_PDO_CASSANDRA_EXTNAME,
+ pdo_cassandra_functions,
+ PHP_MINIT(pdo_cassandra),
+ PHP_MSHUTDOWN(pdo_cassandra),
+ NULL,
+ NULL,
+ PHP_MINFO(pdo_cassandra),
+ PHP_PDO_CASSANDRA_EXTVER,
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#if defined(COMPILE_DL_PDO_CASSANDRA)
+ZEND_GET_MODULE(pdo_cassandra)
+#endif
185 cassandra_statement.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2011 DataStax
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "php_pdo_cassandra.hpp"
+#include "php_pdo_cassandra_int.hpp"
+
+/** {{{ static int pdo_cassandra_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
+*/
+static int pdo_cassandra_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
+{
+ pdo_cassandra_stmt *S = static_cast <pdo_cassandra_stmt *>(stmt->driver_data);
+ pdo_cassandra_db_handle *H = static_cast <pdo_cassandra_db_handle *>(S->H);
+
+ try {
+ if (!H->transport->isOpen()) {
+ H->transport->open();
+ }
+ std::string q = stmt->active_query_string;
+ S->result.reset (new CqlResult);
+ H->client->execute_cql_query(*S->result.get (), q, (H->compression ? Compression::GZIP : Compression::NONE));
+ S->has_iterator = 0;
+ return 1;
+ } catch (NotFoundException &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_NOT_FOUND, "%s", e.what());
+ } catch (InvalidRequestException &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_INVALID_REQUEST, "%s", e.why.c_str());
+ } catch (UnavailableException &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_UNAVAILABLE, "%s", e.what());
+ } catch (TimedOutException &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_TIMED_OUT, "%s", e.what());
+ } catch (AuthenticationException &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_AUTHENTICATION_ERROR, "%s", e.why.c_str());
+ } catch (AuthorizationException &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_AUTHORIZATION_ERROR, "%s", e.why.c_str());
+ } catch (SchemaDisagreementException &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_SCHEMA_DISAGREEMENT, "%s", e.what());
+ } catch (TTransportException &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_TRANSPORT_ERROR, "%s", e.what());
+ } catch (TException &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_GENERAL_ERROR, "%s", e.what());
+ } catch (std::exception &e) {
+ pdo_cassandra_error(stmt->dbh, PDO_CASSANDRA_GENERAL_ERROR, "%s", e.what());
+ }
+ return 0;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
+*/
+static int pdo_cassandra_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
+{
+ pdo_cassandra_stmt *S = static_cast <pdo_cassandra_stmt *>(stmt->driver_data);
+
+ if (!S->has_iterator) {
+ S->it = S->result.get()->rows.begin ();
+ S->has_iterator = 1;
+ } else {
+ S->it++;
+ }
+
+ if (S->it == S->result.get()->rows.end ()) {
+ // Iterated all rows, reset the iterator
+ S->has_iterator = 0;
+ return 0;
+ }
+
+ stmt->column_count = S->result.get()->rows.size ();
+ return 1;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
+*/
+static int pdo_cassandra_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
+{
+ pdo_cassandra_stmt *S = static_cast <pdo_cassandra_stmt *>(stmt->driver_data);
+
+ if (colno < 0 || (colno >= 0 && (static_cast <size_t>(colno) >= (*S->it).columns.size ()))) {
+ return 0;
+ }
+
+ if (!(*S->it).columns[colno].name.size ()) {
+ return 0;
+ }
+
+ stmt->columns[colno].name = estrdup (const_cast <char *> ((*S->it).columns[colno].name.c_str ()));
+ stmt->columns[colno].namelen = (*S->it).columns[colno].name.size ();
+ stmt->columns[colno].maxlen = -1;
+ stmt->columns[colno].precision = 0;
+ stmt->columns[colno].param_type = PDO_PARAM_STR;
+
+ return 1;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_stmt_get_column(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
+*/
+static int pdo_cassandra_stmt_get_column(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
+{
+ pdo_cassandra_stmt *S = static_cast <pdo_cassandra_stmt *>(stmt->driver_data);
+
+ if (colno < 0 || (colno >= 0 && (static_cast <size_t>(colno) >= (*S->it).columns.size ()))) {
+ return 0;
+ }
+ *ptr = const_cast <char *> ((*S->it).columns[colno].value.c_str ());
+ *len = (*S->it).columns[colno].value.size();
+ *caller_frees = 0;
+ return 1;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC)
+*/
+static int pdo_cassandra_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC)
+{
+ pdo_cassandra_stmt *S = static_cast <pdo_cassandra_stmt *>(stmt->driver_data);
+
+ if (colno < 0 || (colno >= 0 && (static_cast <size_t>(colno) >= (*S->it).columns.size ()))) {
+ return FAILURE;
+ }
+ array_init(return_value);
+ add_assoc_string(return_value, "native_type", "string", 1);
+ return SUCCESS;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC)
+*/
+static int pdo_cassandra_stmt_cursor_close(pdo_stmt_t *stmt TSRMLS_DC)
+{
+ pdo_cassandra_stmt *S = static_cast <pdo_cassandra_stmt *>(stmt->driver_data);
+ S->has_iterator = 0;
+ return 1;
+}
+/* }}} */
+
+/** {{{ static int pdo_cassandra_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
+*/
+static int pdo_cassandra_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
+{
+ if (stmt->driver_data) {
+ pdo_cassandra_stmt *S = static_cast <pdo_cassandra_stmt *>(stmt->driver_data);
+ S->result.reset();
+ delete S;
+ stmt->driver_data = NULL;
+ }
+ return 1;
+}
+/* }}} */
+
+struct pdo_stmt_methods cassandra_stmt_methods = {
+ pdo_cassandra_stmt_dtor,
+ pdo_cassandra_stmt_execute,
+ pdo_cassandra_stmt_fetch,
+ pdo_cassandra_stmt_describe,
+ pdo_cassandra_stmt_get_column,
+ NULL, /* param_hook */
+ NULL, /* set_attr */
+ NULL, /* get_attr */
+ pdo_cassandra_stmt_get_column_meta,
+ NULL, /* next_rowset */
+ pdo_cassandra_stmt_cursor_close
+};
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
88 config.m4
@@ -0,0 +1,88 @@
+PHP_ARG_WITH(pdo-cassandra, whether to enable PDO cassandra support,
+[ --with-pdo-cassandra[=FILE] Enable PDO cassandra support. FILE is optional path to thrift interface file.], yes)
+
+PHP_ARG_WITH(thrift-dir, optional thrift install prefix,
+[ --with-thrift-dir[=DIR] Optional path to thrift installation.], no, no)
+
+PHP_ARG_WITH(boost-dir, optional boost install prefix,
+[ --with-boost-dir[=DIR] Optional path to boost installation.], no, no)
+
+if test "x${PHP_PDO_CASSANDRA}" != "xno"; then
+
+ if test "x${PHP_PDO}" = "xno" && test "x${ext_shared}" = "xno"; then
+ AC_MSG_ERROR([PDO is not enabled. Add --enable-pdo to your configure line.])
+ fi
+
+ AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+ if test "x${PKG_CONFIG}" = "xno"; then
+ AC_MSG_RESULT([pkg-config not found])
+ AC_MSG_ERROR([Please reinstall the pkg-config distribution])
+ fi
+
+ ORIG_PKG_CONFIG_PATH="${PKG_CONFIG_PATH}"
+
+ AC_MSG_CHECKING(thrift installation)
+ if test "x${PHP_THRIFT_DIR}" = "xyes" -o "x${PHP_THRIFT_DIR}" = "xno"; then
+ if test "x${PKG_CONFIG_PATH}" != "x"; then
+ export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:/usr/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/lib/pkgconfig:/opt/local/lib/pkgconfig"
+ else
+ export PKG_CONFIG_PATH="/usr/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/lib/pkgconfig:/opt/local/lib/pkgconfig"
+ fi
+ else
+ export PKG_CONFIG_PATH="${PHP_THRIFT_DIR}:${PHP_THRIFT_DIR}/lib/pkgconfig"
+ fi
+
+ if ${PKG_CONFIG} --exists thrift; then
+ PHP_THRIFT_VERSION=`${PKG_CONFIG} thrift --modversion`
+
+ AC_MSG_RESULT([found version ${PHP_THRIFT_VERSION}])
+ PHP_THRIFT_LIBS=`${PKG_CONFIG} thrift --libs`
+ PHP_THRIFT_INCS=`${PKG_CONFIG} thrift --cflags`
+
+ PHP_EVAL_LIBLINE(${PHP_THRIFT_LIBS}, PDO_CASSANDRA_SHARED_LIBADD)
+ PHP_EVAL_INCLINE(${PHP_THRIFT_INCS})
+ else
+ AC_MSG_ERROR(Unable to find thrift installation)
+ fi
+
+ THRIFT_BIN=`${PKG_CONFIG} thrift --variable=prefix`"/bin/thrift"
+
+ if test ! -x "${THRIFT_BIN}"; then
+ AC_MSG_ERROR([${THRIFT_BIN} does not exist or is not executable])
+ fi
+
+ if test "x${PHP_PDO_CASSANDRA}" = "xyes" -o "x${PHP_PDO_CASSANDRA}" = "xno"; then
+ INTERFACE_FILE="interface/cassandra.thrift"
+ else
+ INTERFACE_FILE="${PHP_PDO_CASSANDRA}"
+ fi
+
+ # Regenerate the cpp
+ "${THRIFT_BIN}" -o . -gen cpp "${INTERFACE_FILE}"
+ if test $? != 0; then
+ AC_MSG_ERROR([failed to regenerate thrift interfaces])
+ fi
+
+ # Add boost includes
+ AC_MSG_CHECKING([boost installation])
+ if test "x${PHP_BOOST_DIR}" != "xyes" -a "x${PHP_BOOST_DIR}" != "xno"; then
+ if test ! -r ${PHP_BOOST_DIR}/include/boost/shared_ptr.hpp; then
+ AC_MSG_ERROR([${PHP_BOOST_DIR}/include/boost/shared_ptr.hpp not found])
+ fi
+ else
+ for dir in /usr /usr/local /opt/local; do
+ test -r "${dir}/include/boost/shared_ptr.hpp" && PHP_BOOST_DIR="${dir}" && break
+ done
+ if test "x${PHP_BOOST_DIR}" = "x"; then
+ AC_MSG_ERROR([boost installation not found])
+ fi
+ fi
+ AC_MSG_RESULT([found in ${PHP_BOOST_DIR}])
+ PHP_ADD_INCLUDE(${PHP_BOOST_DIR}/include)
+
+ PHP_ADD_EXTENSION_DEP(pdo_cassandra, pdo)
+ PHP_SUBST(PDO_CASSANDRA_SHARED_LIBADD)
+ PHP_REQUIRE_CXX()
+ PHP_NEW_EXTENSION(pdo_cassandra, cassandra_driver.cpp cassandra_statement.cpp gen-cpp/Cassandra.cpp gen-cpp/cassandra_types.cpp, $ext_shared,,-Wall -Wno-write-strings)
+fi
+
41 doc/pdo_cassandra/configure.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Purpose: database.vendors -->
+<!-- Membership: pecl -->
+<!-- State: experimental -->
+<section xml:id="ref.pdo-cassandra.installation" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
+ &reftitle.install;
+ <para>
+ Use <option role="configure">--with-pdo-cassandra[=FILE]</option> to install
+ the PDO Cassandra extension, where the optional <literal>[=FILE]</literal>
+ is the path to thrift interfaces file.
+ Additional configuration options <option role="configure">--with-thrift-dir[=DIR]</option>
+ and <option role="configure">--with-boost-dir[=DIR]</option> can be used to specify
+ thrift and boost installation prefixes.
+ <screen>
+<![CDATA[
+$ ./configure --with-pdo-cassandra[=FILE] [--with-thrift-dir[=DIR] --with-boost-dir[=dir]]
+]]>
+ </screen>
+ </para>
+</section>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
171 doc/pdo_cassandra/constants.xml
@@ -0,0 +1,171 @@
+<?xml version='1.0' encoding="utf-8"?>
+<!-- Purpose: database.vendors -->
+<!-- Membership: pecl -->
+<!-- State: experimental -->
+<section xml:id="pdo-cassandra.constants" xmlns="http://docbook.org/ns/docbook">
+ &reftitle.constants;
+ &pdo.driver-constants;
+ <variablelist>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_NUM_RETRIES</constant>
+ (<type>integer</type>)
+ </term>
+ <listitem>
+ <para>
+ How many times to try to retry if connection fails.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_RETRY_INTERVAL</constant>
+ (<type>integer</type>)
+ </term>
+ <listitem>
+ <para>
+ Sets how long to wait until retrying a host if it was marked down.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_MAX_CONSECUTIVE_FAILURES</constant>
+ (<type>integer</type>)
+ </term>
+ <listitem>
+ <para>
+ Sets how many times to keep retrying a host before marking it as down.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_RANDOMIZE</constant>
+ (<type>boolean</type>)
+ </term>
+ <listitem>
+ <para>
+ Randomize which host connect to. This option can
+ be used if the DSN contains multiple host and port
+ combinations.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_ALWAYS_TRY_LAST</constant>
+ (<type>boolean</type>)
+ </term>
+ <listitem>
+ <para>
+ Always try to connect to the last host in the list.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_LINGER</constant>
+ (<type>integer</type>)
+ </term>
+ <listitem>
+ <para>
+ How long does the socket linger after it's being closed.
+ Value 0 turns off linger.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_NO_DELAY</constant>
+ (<type>boolean</type>)
+ </term>
+ <listitem>
+ <para>
+ Whether to enable/disable Nagle algorithm.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_CONN_TIMEOUT</constant>
+ (<type>integer</type>)
+ </term>
+ <listitem>
+ <para>
+ The connection timeout value. This driver will
+ try to connect if the connection has been lost so
+ this value can be set to control the timeout after
+ object construction.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_RECV_TIMEOUT</constant>
+ (<type>integer</type>)
+ </term>
+ <listitem>
+ <para>
+ Receive timeout.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_SEND_TIMEOUT</constant>
+ (<type>integer</type>)
+ </term>
+ <listitem>
+ <para>
+ Send timeout.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_COMPRESSION</constant>
+ (<type>boolean</type>)
+ </term>
+ <listitem>
+ <para>
+ Whether to enable compression.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <constant>PDO::CASSANDRA_ATTR_THRIFT_DEBUG</constant>
+ (<type>boolean</type>)
+ </term>
+ <listitem>
+ <para>
+ Turns on thrift debugging. This converts thrift
+ messages into PHP warnings. This option can be passed into the
+ PDO constructor in the fourth argument.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+</section>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
106 doc/pdo_cassandra/reference.xml
@@ -0,0 +1,106 @@
+<?xml version='1.0' encoding="utf-8"?>
+<!-- Purpose: database.vendors -->
+<!-- Membership: pecl -->
+<!-- State: experimental -->
+
+ <reference xml:id="ref.pdo-cassandra" xmlns="http://docbook.org/ns/docbook">
+ <title>Cassandra Functions (PDO_CASSANDRA)</title>
+ <titleabbrev>Cassandra (PDO)</titleabbrev>
+ <partintro>
+
+ <section xml:id="pdo-cassandra.intro">
+ &reftitle.intro;
+ <para>
+ &warn.experimental;
+ PDO_CASSANDRA is a driver that implements the PHP Data Objects (PDO)
+ interface to enable access from PHP to Cassandra database.
+ </para>
+ <para>
+ Due to nature of Cassandra the <link linkend="pdo.exec">exec</link>
+ method doesn't return the number of affected rows for
+ DELETE and UPDATE queries.
+ </para>
+ </section>
+ &reference.pdo-cassandra.configure;
+ &reference.pdo-cassandra.constants;
+ </partintro>
+
+ <refentry xml:id="ref.pdo-cassandra.connection">
+ <refnamediv>
+ <refname>PDO_CASSANDRA DSN</refname>
+ <refpurpose>Connecting to Cassandra databases</refpurpose>
+ </refnamediv>
+
+ <refsect1 role="description">
+ &reftitle.description;
+ <para>
+ The PDO_CASSANDRA Data Source Name (DSN) is
+ composed of the following elements:
+ <variablelist>
+ <varlistentry>
+ <term>DSN prefix</term>
+ <listitem>
+ <para>
+ The DSN prefix is <userinput>cassandra:</userinput>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><constant>host</constant></term>
+ <listitem>
+ <para>
+ The hostname of the database.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><constant>port</constant></term>
+ <listitem>
+ <para>
+ The port of the database.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+ <refsect1 role="examples">
+ &reftitle.examples;
+ <para>
+ <example>
+ <title>PDO_CASSANDRA DSN examples</title>
+ <para>
+ The following examples show PDO_CASSANDRA DSNs for connecting to
+ Cassandra database:
+ <programlisting><![CDATA[
+cassandra:host=localhost;port=9601
+cassandra:host=localhost;port=9601,host=anotherhost;port=9601
+]]>
+ </programlisting>
+ </para>
+ </example>
+ </para>
+ </refsect1>
+ </refentry>
+
+ </reference>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
61 package.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+ <name>pdo_cassandra</name>
+ <channel>pecl.php.net</channel>
+ <summary>PDO Driver for Cassandra CQL</summary>
+ <description>CQL is heavily based on SQL – close to a subset of SQL.</description>
+ <lead>
+ <name>Mikko Koppanen</name>
+ <user>mkoppanen</user>
+ <email>mkoppanen@php.net</email>
+ <active>yes</active>
+ </lead>
+ <date>2011-08-05</date>
+ <time>12:00:00</time>
+ <version>
+ <release>0.1.0</release>
+ <api>0.1.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license>PHP License</license>
+ <notes>
+- Initial release of the extension
+ </notes>
+ <contents>
+ <dir name="/">
+ <!-- Support files -->
+ <file name="config.m4" role="src" />
+ <file name="interface/cassandra.thrift" role="src"/>
+
+ <!-- source files -->
+ <file name="cassandra_driver.cpp" role="src"/>
+ <file name="cassandra_statement.cpp" role="src"/>
+
+ <!-- Headers -->
+ <file name="php_pdo_cassandra.hpp" role="src">
+ <tasks:replace from="@PACKAGE_VERSION@" to="version" type="package-info" />
+ </file>
+ <file name="php_pdo_cassandra_int.hpp" role="src"/>
+
+ <!-- misc files -->
+ <file name="README.md" role="doc" />
+ <file name="LICENSE" role="doc" />
+ <file name="ChangeLog" role="doc" />
+ </dir>
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.3.0</min>
+ </php>
+ <pearinstaller>
+ <min>1.4.0</min>
+ </pearinstaller>
+ </required>
+ </dependencies>
+ <providesextension>pdo_cassandra</providesextension>
+ <extsrcrelease />
+</package>
34 php_pdo_cassandra.hpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 DataStax
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PHP_PDO_CASSANDRA_H_
+# define _PHP_PDO_CASSANDRA_H_
+
+#define PHP_PDO_CASSANDRA_EXTNAME "pdo_cassandra"
+#define PHP_PDO_CASSANDRA_EXTVER "@PACKAGE_VERSION@"
+
+extern "C" {
+#ifdef ZTS
+# include "TSRM.h"
+#endif
+
+#include "php.h"
+}
+
+extern zend_module_entry cassandra_module_entry;
+#define phpext_cassandra_ptr &cassandra_module_entry
+
+#endif /* _PHP_PDO_CASSANDRA_H_ */
127 php_pdo_cassandra_int.hpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2011 DataStax
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PHP_PDO_CASSANDRA_PRIVATE_H_
+# define _PHP_PDO_CASSANDRA_PRIVATE_H_
+
+extern "C" {
+/* Need to undefine these so that thrift config doesn't complain */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# undef PACKAGE_NAME
+# undef PACKAGE_STRING
+# undef PACKAGE_TARNAME
+# undef PACKAGE_VERSION
+#endif
+
+#include "ext/standard/info.h"
+#include "Zend/zend_exceptions.h"
+#include "Zend/zend_interfaces.h"
+#include "main/php_ini.h"
+#include "pdo/php_pdo.h"
+#include "pdo/php_pdo_driver.h"
+#include "ext/standard/url.h"
+#include "ext/standard/php_string.h"
+}
+
+#define HAVE_ZLIB_CP HAVE_ZLIB
+#undef HAVE_ZLIB
+
+#include "gen-cpp/Cassandra.h"
+#include <protocol/TBinaryProtocol.h>
+#include <transport/TSocketPool.h>
+#include <transport/TTransportUtils.h>
+
+#undef HAVE_ZLIB
+#define HAVE_ZLIB HAVE_ZLIB_CP
+
+using namespace apache::thrift;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+using namespace org::apache::cassandra;
+
+/** {{{
+*/
+typedef struct {
+ const char *file;
+ char *errmsg;
+ int line;
+ unsigned int errcode;
+} pdo_cassandra_einfo;
+/* }}} */
+
+/* {{{ typedef struct php_cassandra_db_handle
+*/
+typedef struct {
+ zend_object zo;
+ zend_bool compression;
+ boost::shared_ptr<TSocketPool> socket;
+ boost::shared_ptr<TFramedTransport> transport;
+ boost::shared_ptr<TProtocol> protocol;
+ boost::shared_ptr<CassandraClient> client;
+ pdo_cassandra_einfo einfo;
+} pdo_cassandra_db_handle;
+/* }}} */
+
+/* {{{ typedef struct pdo_cassandra_stmt
+*/
+typedef struct {
+ pdo_cassandra_db_handle *H;
+ zend_bool has_iterator;
+ boost::shared_ptr<CqlResult> result;
+ std::vector<CqlRow>::iterator it;
+} pdo_cassandra_stmt;
+/* }}} */
+
+/* {{{ enum pdo_cassandra_constant
+*/
+enum pdo_cassandra_constant {
+ PDO_CASSANDRA_ATTR_MIN = PDO_ATTR_DRIVER_SPECIFIC,
+ PDO_CASSANDRA_ATTR_NUM_RETRIES,
+ PDO_CASSANDRA_ATTR_RETRY_INTERVAL,
+ PDO_CASSANDRA_ATTR_MAX_CONSECUTIVE_FAILURES,
+ PDO_CASSANDRA_ATTR_RANDOMIZE,
+ PDO_CASSANDRA_ATTR_ALWAYS_TRY_LAST,
+ PDO_CASSANDRA_ATTR_LINGER,
+ PDO_CASSANDRA_ATTR_NO_DELAY,
+ PDO_CASSANDRA_ATTR_CONN_TIMEOUT,
+ PDO_CASSANDRA_ATTR_RECV_TIMEOUT,
+ PDO_CASSANDRA_ATTR_SEND_TIMEOUT,
+ PDO_CASSANDRA_ATTR_COMPRESSION,
+ PDO_CASSANDRA_ATTR_THRIFT_DEBUG,
+ PDO_CASSANDRA_ATTR_MAX
+};
+/* }}} */
+
+enum pdo_cassandra_error {
+ PDO_CASSANDRA_GENERAL_ERROR,
+ PDO_CASSANDRA_NOT_FOUND,
+ PDO_CASSANDRA_INVALID_REQUEST,
+ PDO_CASSANDRA_UNAVAILABLE,
+ PDO_CASSANDRA_TIMED_OUT,
+ PDO_CASSANDRA_AUTHENTICATION_ERROR,
+ PDO_CASSANDRA_AUTHORIZATION_ERROR,
+ PDO_CASSANDRA_SCHEMA_DISAGREEMENT,
+ PDO_CASSANDRA_TRANSPORT_ERROR,
+ PDO_CASSANDRA_INVALID_CONNECTION_STRING
+};
+
+void pdo_cassandra_error_ex(pdo_dbh_t *dbh TSRMLS_DC, pdo_cassandra_error code, const char *file, int line, zend_bool force_exception, const char *message, ...);
+#define pdo_cassandra_error(dbh, code, message, ...) pdo_cassandra_error_ex(dbh TSRMLS_CC, code, __FILE__, __LINE__, 0, message, __VA_ARGS__)
+#define pdo_cassandra_error_exception(dbh, code, message, ...) pdo_cassandra_error_ex(dbh TSRMLS_CC, code, __FILE__, __LINE__, 1, message, __VA_ARGS__)
+
+
+#endif /* _PHP_PDO_CASSANDRA_PRIVATE_H_ */
16 tests/001-construct.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Test pdo cassandra construction
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+echo "OK";
+
+?>
+--EXPECT--
+OK
33 tests/002-select.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test prepared statement emulation
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+pdo_cassandra_init ($db, $keyspace);
+
+$stmt = $db->prepare ("SELECT my_key, full_name FROM users WHERE my_key = :key;");
+$stmt->bindValue (':key', 'mikko');
+$stmt->execute ();
+
+var_dump ($stmt->fetchAll ());
+
+pdo_cassandra_done ($db, $keyspace);
+
+echo "OK";
+
+--EXPECT--
+array(1) {
+ [0]=>
+ array(2) {
+ ["my_key"]=>
+ string(5) "mikko"
+ [0]=>
+ string(5) "mikko"
+ }
+}
+OK
21 tests/003-transaction.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Test transaction
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+try {
+ $db->beginTransaction();
+} catch (PDOException $e) {
+ echo $e->getMessage () . PHP_EOL;
+}
+
+echo "OK";
+
+--EXPECT--
+This driver doesn't support transactions
+OK
35 tests/004-columnmeta.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Test column metadata
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+pdo_cassandra_init ($db, $keyspace);
+
+$stmt = $db->query ("SELECT my_key, full_name FROM users;");
+$stmt->fetch ();
+var_dump ($stmt->getColumnMeta (0));
+var_dump ($stmt->getColumnMeta (3));
+
+pdo_cassandra_done ($db, $keyspace);
+
+echo "OK";
+--EXPECT--
+array(5) {
+ ["native_type"]=>
+ string(6) "string"
+ ["name"]=>
+ string(6) "my_key"
+ ["len"]=>
+ int(-1)
+ ["precision"]=>
+ int(0)
+ ["pdo_type"]=>
+ int(2)
+}
+bool(false)
+OK
19 tests/005-attributes.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test setting/getting attributes
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+$db = new PDO($dsn);
+
+var_dump ($db->getAttribute (PDO::ATTR_SERVER_VERSION));
+var_dump ($db->setAttribute (PDO::CASSANDRA_ATTR_NUM_RETRIES, 10));
+var_dump ($db->setAttribute (PDO::CASSANDRA_ATTR_NO_DELAY, true));
+
+echo "OK";
+--EXPECTF--
+string(7) "%d.%d.%d"
+bool(true)
+bool(true)
+OK
68 tests/006-iterate.phpt
@@ -0,0 +1,68 @@
+--TEST--
+Test iterating prepared statement
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+pdo_cassandra_init ($db, $keyspace);
+
+$result = $db->query ("SELECT my_key, full_name FROM users;");
+
+foreach ($result as $row) {
+ var_dump ($row);
+}
+
+foreach ($result as $row) {
+ var_dump ($row);
+}
+
+pdo_cassandra_done ($db, $keyspace);
+
+echo "OK";
+
+--EXPECT--
+array(4) {
+ ["my_key"]=>
+ string(5) "mikko"
+ [0]=>
+ string(5) "mikko"
+ ["full_name"]=>
+ string(7) "Mikko K"
+ [1]=>
+ string(7) "Mikko K"
+}
+array(4) {
+ ["my_key"]=>
+ string(4) "john"
+ [0]=>
+ string(4) "john"
+ ["full_name"]=>
+ string(8) "John Doe"
+ [1]=>
+ string(8) "John Doe"
+}
+array(4) {
+ ["my_key"]=>
+ string(5) "mikko"
+ [0]=>
+ string(5) "mikko"
+ ["full_name"]=>
+ string(7) "Mikko K"
+ [1]=>
+ string(7) "Mikko K"
+}
+array(4) {
+ ["my_key"]=>
+ string(4) "john"
+ [0]=>
+ string(4) "john"
+ ["full_name"]=>
+ string(8) "John Doe"
+ [1]=>
+ string(8) "John Doe"
+}
+OK
43 tests/007-fetchgrouped.phpt
@@ -0,0 +1,43 @@
+--TEST--
+Test fetching grouped columns
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+pdo_cassandra_init ($db, $keyspace);
+
+$result = $db->query ("SELECT my_key, full_name FROM users;");
+var_dump($result->fetchAll (PDO::FETCH_GROUP));
+
+pdo_cassandra_done ($db, $keyspace);
+
+echo "OK";
+
+--EXPECT--
+array(2) {
+ ["mikko"]=>
+ array(1) {
+ [0]=>
+ array(2) {
+ ["full_name"]=>
+ string(7) "Mikko K"
+ [0]=>
+ string(7) "Mikko K"
+ }
+ }
+ ["john"]=>
+ array(1) {
+ [0]=>
+ array(2) {
+ ["full_name"]=>
+ string(8) "John Doe"
+ [0]=>
+ string(8) "John Doe"
+ }
+ }
+}
+OK
71 tests/008-executemultiple.phpt
@@ -0,0 +1,71 @@
+--TEST--
+Test execute prepared statement multiple times
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+pdo_cassandra_init ($db, $keyspace);
+
+$result = $db->query ("SELECT my_key, full_name FROM users;");
+var_dump($result->fetchAll (PDO::FETCH_GROUP));
+$result->closeCursor ();
+
+$result->execute ();
+var_dump($result->fetchAll (PDO::FETCH_GROUP));
+
+pdo_cassandra_done ($db, $keyspace);
+
+echo "OK";
+
+?>
+--EXPECT--
+array(2) {
+ ["mikko"]=>
+ array(1) {
+ [0]=>
+ array(2) {
+ ["full_name"]=>
+ string(7) "Mikko K"
+ [0]=>
+ string(7) "Mikko K"
+ }
+ }
+ ["john"]=>
+ array(1) {
+ [0]=>
+ array(2) {
+ ["full_name"]=>
+ string(8) "John Doe"
+ [0]=>
+ string(8) "John Doe"
+ }
+ }
+}
+array(2) {
+ ["mikko"]=>
+ array(1) {
+ [0]=>
+ array(2) {
+ ["full_name"]=>
+ string(7) "Mikko K"
+ [0]=>
+ string(7) "Mikko K"
+ }
+ }
+ ["john"]=>
+ array(1) {
+ [0]=>
+ array(2) {
+ ["full_name"]=>
+ string(8) "John Doe"
+ [0]=>
+ string(8) "John Doe"
+ }
+ }
+}
+OK
26 tests/009-invaliddsn.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Test invalid dsn
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+try {
+ $db = new PDO('cassandra:asasd:AsdA:DSasd;A:sdasd');
+ echo 'fail';
+} catch (PDOException $e) {
+ echo $e->getMessage () . PHP_EOL;
+}
+
+try {
+ $db = new PDO('cassandra:');
+ echo 'fail';
+} catch (PDOException $e) {
+ echo $e->getMessage () . PHP_EOL;
+}
+echo "OK";
+
+?>
+--EXPECT--
+CQLSTATE[HY000] [9] Invalid connection string attribute
+CQLSTATE[HY000] [9] Invalid connection string attribute
+OK
14 tests/010-auth.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test authentication
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn, 'mikko', 'okkim');
+echo "OK";
+
+?>
+--EXPECT--
+OK
24 tests/011-persistentconnection.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Test initialization of persistent connections
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn, null, null, array (
+ PDO::ATTR_PERSISTENT => true
+ ));
+
+echo "OK";
+
+$db = null;
+
+$db1 = new PDO($dsn, null, null, array (
+ PDO::ATTR_PERSISTENT => true
+ ));
+
+?>
+--EXPECT--
+OK
24 tests/012-quoter.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Test quoting values
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+var_dump ($db->quote ("'hello' 'world'"));
+var_dump ($db->quote ("Co'mpl''ex \"st'\"ring"));
+var_dump ($db->quote ("'''''''''", PDO::PARAM_LOB));
+var_dump ($db->quote ("test " . chr(0) . " value"));
+
+
+echo "OK";
+?>
+--EXPECT--
+string(21) "'\'hello\' \'world\''"
+string(28) "'Co\'mpl\'\'ex \"st\'\"ring'"
+string(20) "'\'\'\'\'\'\'\'\'\''"
+string(15) "'test \0 value'"
+OK
57 tests/013-errorhandling.phpt
@@ -0,0 +1,57 @@
+--TEST--
+Test different error handling modes
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+pdo_cassandra_init ($db, $keyspace);
+
+echo "-- SILENT -- " . PHP_EOL;
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
+$db->exec ("CREATE KEYSPACE $keyspace with strategy_class = 'SimpleStrategy' and strategy_options:replication_factor=1;");
+var_dump ($db->errorInfo ());
+echo "-- SILENT -- " . PHP_EOL;
+
+echo "-- WARNING -- " . PHP_EOL;
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
+$db->exec ("CREATE KEYSPACE $keyspace with strategy_class = 'SimpleStrategy' and strategy_options:replication_factor=1;");
+echo "-- WARNING -- " . PHP_EOL;
+
+echo "-- EXCEPTION -- " . PHP_EOL;
+try {
+ $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $db->exec ("CREATE KEYSPACE $keyspace with strategy_class = 'SimpleStrategy' and strategy_options:replication_factor=1;");
+} catch (PDOException $e) {
+ echo $e->getMessage () . PHP_EOL;
+}
+echo "-- EXCEPTION -- " . PHP_EOL;
+
+pdo_cassandra_done ($db, $keyspace);
+
+echo "OK";
+
+?>
+--EXPECTF--
+-- SILENT --
+array(3) {
+ [0]=>
+ string(5) "HY000"
+ [1]=>
+ int(2)
+ [2]=>
+ string(24) "Keyspace already exists."
+}
+-- SILENT --
+-- WARNING --
+
+Warning: PDO::exec(): CQLSTATE[HY000] [2] Keyspace already exists. in %s on line %d
+-- WARNING --
+-- EXCEPTION --
+CQLSTATE[HY000] [2] Keyspace already exists.
+-- EXCEPTION --
+OK
50 tests/014-affectedrows.phpt
@@ -0,0 +1,50 @@
+--TEST--
+Test number of affected rows
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+pdo_cassandra_init ($db, $keyspace);
+
+var_dump ($db->exec ("UPDATE users SET full_name = 'K okkiM' WHERE my_key = 'mikko'"));
+
+$stmt = $db->query ("SELECT my_key, full_name FROM users");
+var_dump ($stmt->fetchAll ());
+
+var_dump ($db->exec ("DELETE FROM users WHERE my_key = 'mikko'"));
+
+pdo_cassandra_done ($db, $keyspace);
+
+echo "OK";
+--EXPECT--
+int(0)
+array(2) {
+ [0]=>
+ array(4) {
+ ["my_key"]=>
+ string(5) "mikko"
+ [0]=>
+ string(5) "mikko"
+ ["full_name"]=>
+ string(7) "K okkiM"
+ [1]=>
+ string(7) "K okkiM"
+ }
+ [1]=>
+ array(4) {
+ ["my_key"]=>
+ string(4) "john"
+ [0]=>
+ string(4) "john"
+ ["full_name"]=>
+ string(8) "John Doe"
+ [1]=>
+ string(8) "John Doe"
+ }
+}
+int(0)
+OK
25 tests/015-lastinsertid.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Test last insert id
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+pdo_cassandra_init ($db, $keyspace);
+
+try {
+ $db->lastInsertId ();
+ echo 'fail';
+} catch (PDOException $e) {
+ echo 'success' . PHP_EOL;
+}
+
+pdo_cassandra_done ($db, $keyspace);
+
+echo "OK";
+--EXPECT--
+success
+OK
56 tests/016-bindparam.phpt
@@ -0,0 +1,56 @@
+--TEST--
+Test binding params
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+require_once(dirname(__FILE__) . '/config.inc');
+
+$db = new PDO($dsn);
+
+pdo_cassandra_init ($db, $keyspace);
+
+$full_name = "test 1";
+
+$stmt = $db->prepare ("UPDATE users SET full_name = :value WHERE my_key = 'mikko'");
+$stmt->bindParam (":value", $full_name, PDO::PARAM_STR);
+$stmt->execute ();
+
+$full_name = "test 2";
+$stmt->execute ();
+
+$full_name = "test 3";
+$stmt->execute ();
+
+$stmt = $db->query ("SELECT my_key, full_name FROM users");
+var_dump ($stmt->fetchAll ());
+
+pdo_cassandra_done ($db, $keyspace);
+
+echo "OK";
+--EXPECT--
+array(2) {
+ [0]=>
+ array(4) {
+ ["my_key"]=>
+ string(5) "mikko"
+ [0]=>
+ string(5) "mikko"
+ ["full_name"]=>
+ string(6) "test 3"
+ [1]=>
+ string(6) "test 3"
+ }
+ [1]=>
+ array(4) {
+ ["my_key"]=>
+ string(4) "john"
+ [0]=>
+ string(4) "john"
+ ["full_name"]=>
+ string(8) "John Doe"
+ [1]=>
+ string(8) "John Doe"
+ }
+}
+OK
3 tests/017-syntaxerror.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+/Users/mkoppanen/Documents/projects/local//bin/php -n -c '/Users/mkoppanen/Documents/projects/php-pdo_cassandra/tmp-php.ini' -d "output_handler=" -d "open_basedir=" -d "safe_mode=0" -d "disable_functions=" -d "output_buffering=Off" -d "error_reporting=32767" -d "display_errors=1" -d "display_startup_errors=1" -d "log_errors=0" -d "html_errors=0" -d "track_errors=1" -d "report_memleaks=1" -d "report_zend_debug=0" -d "docref_root=" -d "docref_ext=.html" -d "error_prepend_string=" -d "error_append_string=" -d "auto_prepend_file=" -d "auto_append_file=" -d "magic_quotes_runtime=0" -d "ignore_repeated_errors=0" -d "precision=14" -d "unicode.runtime_encoding=ISO-8859-1" -d "unicode.script_encoding=UTF-8" -d "unicode.output_encoding=UTF-8" -d "unicode.from_error_mode=U_INVALID_SUBSTITUTE" -d "extension_dir=/Users/mkoppanen/Documents/projects/php-pdo_cassandra/modules/" -d "extension=pdo_cassandra.so" -d "session.auto_start=0" -f "/Users/mkoppanen/Documents/projects/php-pdo_cassandra/tests/017-syntaxerror.php" 2>&1
33 tests/config.inc-dist
@@ -0,0 +1,33 @@
+<?php
+// DSN to use for tests