[ --enable-pdo Enable PHP Data Objects support])
if test "$PHP_PDO" != "no"; then
- PHP_NEW_EXTENSION(pdo, pdo.c pdo_dbh.c pdo_stmt.c pdo_sql_parser.c, $ext_shared)
+ PHP_NEW_EXTENSION(pdo, pdo.c pdo_dbh.c pdo_stmt.c pdo_sql_parser.c pdo_sqlstate.c, $ext_shared)
PHP_ADD_MAKEFILE_FRAGMENT
fi
ARG_ENABLE("pdo", "Enable PHP Data Objects support", "no");
if (PHP_PDO != "no") {
- EXTENSION('pdo', 'pdo.c pdo_dbh.c pdo_stmt.c pdo_sql_parser.c');
+ EXTENSION('pdo', 'pdo.c pdo_dbh.c pdo_stmt.c pdo_sql_parser.c pdo_sqlstate.c');
}
ZEND_INIT_MODULE_GLOBALS(pdo, php_pdo_init_globals, NULL);
REGISTER_INI_ENTRIES();
+ if (FAILURE == pdo_sqlstate_init_error_table()) {
+ return FAILURE;
+ }
+
zend_hash_init(&pdo_driver_hash, 0, NULL, NULL, 1);
le_ppdo = zend_register_list_destructors_ex(NULL, php_pdo_pdbh_dtor,
REGISTER_LONG_CONSTANT("PDO_CASE_LOWER", (long)PDO_CASE_LOWER, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_CASE_UPPER", (long)PDO_CASE_UPPER, CONST_CS|CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("PDO_ERR_NONE", (long)PDO_ERR_NONE, CONST_CS|CONST_PERSISTENT);
+ REGISTER_STRING_CONSTANT("PDO_ERR_NONE", PDO_ERR_NONE, CONST_CS|CONST_PERSISTENT);
+
+#if 0
REGISTER_LONG_CONSTANT("PDO_ERR_CANT_MAP", (long)PDO_ERR_CANT_MAP, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_ERR_SYNTAX", (long)PDO_ERR_SYNTAX, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_ERR_CONSTRAINT", (long)PDO_ERR_CONSTRAINT, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_ERR_TRUNCATED", (long)PDO_ERR_TRUNCATED, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_ERR_DISCONNECTED", (long)PDO_ERR_DISCONNECTED, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_ERR_NO_PERM", (long)PDO_ERR_NO_PERM, CONST_CS|CONST_PERSISTENT);
+#endif
INIT_CLASS_ENTRY(ce, "PDOException", NULL);
pdo_exception_ce = zend_register_internal_class_ex(&ce, zend_exception_get_default(), NULL TSRMLS_CC);
{
UNREGISTER_INI_ENTRIES();
zend_hash_destroy(&pdo_driver_hash);
+ pdo_sqlstate_fini_error_table();
return SUCCESS;
}
/* }}} */
<?php
+dl('pdo.so');
+dl('pdo_sqlite.so');
//$x = new PDO("oci:dbname=hostname", 'php', 'php');
-$x = new PDO("odbc:ram", 'php', 'php', array(PDO_ATTR_AUTOCOMMIT => 0));
-$stmt = $x->prepare("select NAME, VALUE from test where value like ?");
-
-$the_name = 'bar%';
-$stmt->execute(array($the_name)) or die("failed to execute!");
-$stmt->bindColumn('VALUE', $value);
-
-while ($row = $stmt->fetch()) {
- echo "name=$row[NAME] value=$row[VALUE]\n";
- echo "value is $value\n";
- echo "\n";
-}
+$x = new PDO("sqlite::memory:");
-echo "Let's try an update\n";
+$x->query("create table test(name string, value string)");
+debug_zval_dump($x);
$stmt = $x->prepare("INSERT INTO test (NAME, VALUE) VALUES (:name, :value)");
}
}
+$stmt = null;
+
+echo "DEFAULT:\n";
+foreach ($x->queryAndIterate("select NAME, VALUE from test") as $row) {
+ print_r($row);
+}
+
+echo "OBJ:\n";
+
+class Foo {
+ public $NAME = "Don't change me";
+}
+
+$foo = new foo;
+
+foreach ($x->queryAndIterate("select NAME, VALUE from test", PDO_FETCH_COLUMN, 1) as $row) {
+ debug_zval_dump($row);
+}
+
+echo "Done\n";
+exit;
+
+$stmt = $x->prepare("select NAME, VALUE from test where value like ?");
+$the_name = 'bar%';
+$stmt->execute(array($the_name)) or die("failed to execute!");
+$stmt->bindColumn('VALUE', $value);
+
+while ($row = $stmt->fetch()) {
+ echo "name=$row[NAME] value=$row[VALUE]\n";
+ echo "value is $value\n";
+ echo "\n";
+}
+
+echo "Let's try an update\n";
+
echo "All done\n";
?>
#include "php_pdo_int.h"
#include "zend_exceptions.h"
#include "zend_object_handlers.h"
+#include "zend_hash.h"
void pdo_handle_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt TSRMLS_DC)
{
- enum pdo_error_type *pdo_err = &dbh->error_code;
+ pdo_error_type *pdo_err = &dbh->error_code;
const char *msg = "<<Unknown>>";
char *supp = NULL;
long native_code = 0;
pdo_err = &stmt->error_code;
}
- switch (*pdo_err) {
- case PDO_ERR_NONE: msg = "No error"; break;
- case PDO_ERR_CANT_MAP: msg = "Consult errorInfo() for more details"; break;
- case PDO_ERR_SYNTAX: msg = "Syntax Error"; break;
- case PDO_ERR_CONSTRAINT:msg = "Constraint violation"; break;
- case PDO_ERR_NOT_FOUND: msg = "Not found"; break;
- case PDO_ERR_ALREADY_EXISTS: msg = "Already exists"; break;
- case PDO_ERR_NOT_IMPLEMENTED: msg = "Not Implemented"; break;
- case PDO_ERR_MISMATCH: msg = "Mismatch"; break;
- case PDO_ERR_TRUNCATED: msg = "Truncated"; break;
- case PDO_ERR_DISCONNECTED: msg = "Disconnected"; break;
- case PDO_ERR_NO_PERM: msg = "No permission"; break;
- default: msg = "<<Invalid>>";
+ /* hash sqlstate to error messages */
+ msg = pdo_sqlstate_state_to_description(*pdo_err);
+ if (!msg) {
+ msg = "<<Unknown error>>";
}
if (dbh->methods->fetch_err) {
MAKE_STD_ZVAL(info);
array_init(info);
- add_next_index_long(info, *pdo_err);
+ add_next_index_string(info, *pdo_err, 1);
if (dbh->methods->fetch_err(dbh, stmt, info TSRMLS_CC)) {
zval **item;
}
}
- if (supp && *pdo_err == PDO_ERR_CANT_MAP) {
- spprintf(&message, 0, "%ld %s", native_code, supp);
- } else if (supp) {
- spprintf(&message, 0, "%s: %ld %s", msg, native_code, supp);
+ if (supp) {
+ spprintf(&message, 0, "SQLSTATE[%s]: %s: %ld %s", *pdo_err, msg, native_code, supp);
} else {
- spprintf(&message, 0, "%s", msg);
+ spprintf(&message, 0, "SQLSTATE[%s]: %s", *pdo_err, msg);
}
if (dbh->error_mode == PDO_ERRMODE_WARNING) {
object_init_ex(ex, pdo_ex);
zend_update_property_string(def_ex, ex, "message", sizeof("message")-1, message TSRMLS_CC);
- zend_update_property_long(def_ex, ex, "code", sizeof("code")-1, *pdo_err TSRMLS_CC);
+ zend_update_property_string(def_ex, ex, "code", sizeof("code")-1, *pdo_err TSRMLS_CC);
if (info) {
zend_update_property(pdo_ex, ex, "errorInfo", sizeof("errorInfo")-1, info TSRMLS_CC);
snprintf(alt_dsn, sizeof(alt_dsn), "pdo.dsn.%s", data_source);
if (FAILURE == cfg_get_string(alt_dsn, &ini_dsn)) {
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_SYNTAX TSRMLS_CC, "invalid data source name");
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "invalid data source name");
ZVAL_NULL(object);
return;
}
colon = strchr(data_source, ':');
if (!colon) {
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_SYNTAX TSRMLS_CC, "invalid data source name (via INI: %s)", alt_dsn);
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "invalid data source name (via INI: %s)", alt_dsn);
ZVAL_NULL(object);
return;
}
/* the specified URI holds connection details */
data_source = dsn_from_uri(data_source, alt_dsn, sizeof(alt_dsn) TSRMLS_CC);
if (!data_source) {
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_SYNTAX TSRMLS_CC, "invalid data source URI");
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "invalid data source URI");
ZVAL_NULL(object);
return;
}
colon = strchr(data_source, ':');
if (!colon) {
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_SYNTAX TSRMLS_CC, "invalid data source name (via uri)");
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "invalid data source name (via URI)");
ZVAL_NULL(object);
return;
}
if (!driver) {
/* NB: don't want to include the data_source in the error message as
* it might contain a password */
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NOT_FOUND TSRMLS_CC, "could not find driver");
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "could not find driver");
ZVAL_NULL(object);
return;
}
/* nope... need to kill it */
pdbh = NULL;
}
-
}
}
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
if (dbh->in_txn) {
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_ALREADY_EXISTS TSRMLS_CC, "There is already an active transaction");
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "There is already an active transaction");
RETURN_FALSE;
}
if (!dbh->methods->begin) {
/* TODO: this should be an exception; see the auto-commit mode
* comments below */
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NOT_IMPLEMENTED TSRMLS_CC, "This driver doesn't support transactions");
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "This driver doesn't support transactions");
RETURN_FALSE;
}
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!dbh->in_txn) {
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NONE TSRMLS_CC, "There is no active transaction");
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "There is no active transaction");
RETURN_FALSE;
}
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!dbh->in_txn) {
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NOT_FOUND TSRMLS_CC, "There is no active transaction");
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "There is no active transaction");
RETURN_FALSE;
}
dbh->error_mode = Z_LVAL_P(value);
RETURN_TRUE;
default:
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_SYNTAX TSRMLS_CC, "Error mode %d is invalid", Z_LVAL_P(value));
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "Error mode %d is invalid", Z_LVAL_P(value));
}
RETURN_FALSE;
dbh->desired_case = Z_LVAL_P(value);
RETURN_TRUE;
default:
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_SYNTAX TSRMLS_CC, "Case folding mode %d is invalid", Z_LVAL_P(value));
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "Case folding mode %d is invalid", Z_LVAL_P(value));
}
RETURN_FALSE;
fail:
if (attr == PDO_ATTR_AUTOCOMMIT) {
- zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NONE TSRMLS_CC, "The auto-commit mode cannot be changed for this driver");
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "The auto-commit mode cannot be changed for this driver");
} else if (!dbh->methods->set_attribute) {
/* XXX: do something better here */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "This driver doesn't support setting attributes");
}
/* }}} */
-/* {{{ proto long PDO::query(string query)
+/* {{{ proto long PDO::exec(string query)
Execute a query that does not return a row set, returning the number of affected rows */
-static PHP_METHOD(PDO, query)
+static PHP_METHOD(PDO, exec)
{
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
char *statement;
RETURN_LONG(ret);
}
}
-
-static PHP_METHOD(PDO, exec) /* FIXME: nuke on release */
-{
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "This is name deprecated, use PDO::query instead");
- PHP_FN(PDO_query)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
-}
/* }}} */
}
/* }}} */
-/* {{{ proto int PDO::errorCode()
+/* {{{ proto string PDO::errorCode()
Fetch the error code associated with the last operation on the database handle */
static PHP_METHOD(PDO, errorCode)
{
RETURN_FALSE;
}
- RETURN_LONG(dbh->error_code);
+ RETURN_STRING(dbh->error_code, 1);
}
/* }}} */
}
array_init(return_value);
- add_next_index_long(return_value, dbh->error_code);
+ add_next_index_string(return_value, dbh->error_code, 1);
if (dbh->methods->fetch_err) {
dbh->methods->fetch_err(dbh, NULL, return_value TSRMLS_CC);
}
/* }}} */
-/* {{{ proto object PDO::queryAndIterate(string sql [, PDOStatement::setFetchMode() args])
+/* {{{ proto object PDO::query(string sql [, PDOStatement::setFetchMode() args])
Prepare and execute $sql; returns the statement object for iteration */
-static PHP_METHOD(PDO, queryAndIterate)
+static PHP_METHOD(PDO, query)
{
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
pdo_stmt_t *stmt;
PHP_ME(PDO, commit, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, rollBack, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, setAttribute, NULL, ZEND_ACC_PUBLIC)
- PHP_ME(PDO, exec, NULL, ZEND_ACC_PUBLIC) /* FIXME: nuke on stable release */
+ PHP_ME(PDO, exec, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, query, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, lastInsertId, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, errorCode, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, errorInfo, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, getAttribute, NULL, ZEND_ACC_PUBLIC)
- PHP_ME(PDO, queryAndIterate,NULL, ZEND_ACC_PUBLIC)
-
{NULL, NULL, NULL}
};
return NULL;
}
+int pdo_hash_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
+{
+ function_entry *funcs;
+ zend_function func;
+ zend_internal_function *ifunc = (zend_internal_function*)&func;
+ int namelen;
+ char *lc_name;
+
+ if (!dbh->methods->get_driver_methods) {
+ return 0;
+ }
+ funcs = dbh->methods->get_driver_methods(dbh,
+ PDO_DBH_DRIVER_METHOD_KIND_DBH TSRMLS_DC);
+ if (!funcs) {
+ return 0;
+ }
+
+ dbh->cls_methods[kind] = pemalloc(sizeof(HashTable), dbh->is_persistent);
+ zend_hash_init_ex(dbh->cls_methods[kind], 8, NULL, NULL, dbh->is_persistent, 0);
+
+ while (funcs->fname) {
+ ifunc->handler = funcs->handler;
+ ifunc->function_name = funcs->fname;
+ ifunc->scope = dbh->ce;
+ ifunc->prototype = NULL;
+ if (funcs->arg_info) {
+ ifunc->arg_info = funcs->arg_info + 1;
+ ifunc->num_args = funcs->num_args;
+ if (funcs->arg_info[0].required_num_args == -1) {
+ ifunc->required_num_args = funcs->num_args;
+ } else {
+ ifunc->required_num_args = funcs->arg_info[0].required_num_args;
+ }
+ ifunc->pass_rest_by_reference = funcs->arg_info[0].pass_by_reference;
+ ifunc->return_reference = funcs->arg_info[0].return_reference;
+ } else {
+ ifunc->arg_info = NULL;
+ ifunc->num_args = 0;
+ ifunc->required_num_args = 0;
+ ifunc->pass_rest_by_reference = 0;
+ ifunc->return_reference = 0;
+ }
+ if (funcs->flags) {
+ ifunc->fn_flags = funcs->flags;
+ } else {
+ ifunc->fn_flags = ZEND_ACC_PUBLIC;
+ }
+ namelen = strlen(funcs->fname);
+ lc_name = emalloc(namelen+1);
+ zend_str_tolower_copy(lc_name, funcs->fname, namelen);
+ zend_hash_add(dbh->cls_methods[kind], lc_name, namelen+1, &func, sizeof(func), NULL);
+ efree(lc_name);
+ funcs++;
+ }
+
+ return 1;
+}
+
static union _zend_function *dbh_method_get(
#if PHP_API_VERSION >= 20041225
zval **object_pp,
#endif
char *method_name, int method_len TSRMLS_DC)
{
- zend_function *fbc;
+ zend_function *fbc = NULL;
char *lc_method_name;
#if PHP_API_VERSION >= 20041225
zval *object = *object_pp;
zend_str_tolower_copy(lc_method_name, method_name, method_len);
if (zend_hash_find(&dbh->ce->function_table, lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
- efree(lc_method_name);
- return NULL;
+
+ /* not a pre-defined method, nor a user-defined method; check
+ * the driver specific methods */
+ if (!dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH]) {
+ if (!pdo_hash_methods(dbh, PDO_DBH_DRIVER_METHOD_KIND_DBH TSRMLS_CC)) {
+ goto out;
+ }
+ }
+
+ if (zend_hash_find(dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH],
+ lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
+ fbc = NULL;
+ goto out;
+ }
+ /* got it */
}
+out:
efree(lc_method_name);
return fbc;
}
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2004 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.0 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_0.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Wez Furlong <wez@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_pdo.h"
+#include "php_pdo_driver.h"
+
+struct pdo_sqlstate_info {
+ char state[6];
+ const char *desc;
+};
+
+static HashTable err_hash;
+
+static struct pdo_sqlstate_info err_initializer[] = {
+ { "00000", "No error" },
+ { "01001", "Cursor operation conflict" },
+ { "01002", "Disconnect error" },
+ { "01003", "NULL value eliminated in set function" },
+ { "01004", "String data, right truncated" },
+ { "01006", "Privilege not revoked" },
+ { "01007", "Privilege not granted" },
+ { "01S00", "Invalid connection string attribute" },
+ { "01S01", "Error in row" },
+ { "01S02", "Option value changed" },
+ { "01S06", "Attempt to fetch before the result set returned the first rowset" },
+ { "01S07", "Fractional truncation" },
+ { "01S08", "Error saving File DSN" },
+ { "01S09", "Invalid keyword" },
+ { "07002", "COUNT field incorrect" },
+ { "07005", "Prepared statement not a cursor-specification" },
+ { "07006", "Restricted data type attribute violation" },
+ { "07009", "Invalid descriptor index" },
+ { "07S01", "Invalid use of default parameter" },
+ { "08001", "Client unable to establish connection" },
+ { "08002", "Connection name in use" },
+ { "08003", "Connection does not exist" },
+ { "08004", "Server rejected the connection" },
+ { "08007", "Connection failure during transaction" },
+ { "08S01", "Communication link failure" },
+ { "21S01", "Insert value list does not match column list" },
+ { "21S02", "Degree of derived table does not match column list" },
+ { "22001", "String data, right truncated" },
+ { "22002", "Indicator variable required but not supplied" },
+ { "22003", "Numeric value out of range" },
+ { "22007", "Invalid datetime format" },
+ { "22008", "Datetime field overflow" },
+ { "22012", "Division by zero" },
+ { "22015", "Interval field overflow" },
+ { "22018", "Invalid character value for cast specification" },
+ { "22019", "Invalid escape character" },
+ { "22025", "Invalid escape sequence" },
+ { "22026", "String data, length mismatch" },
+ { "23000", "Integrity constraint violation" },
+ { "24000", "Invalid cursor state" },
+ { "25000", "Invalid transaction state" },
+ { "25S01", "Transaction state" },
+ { "25S02", "Transaction is still active" },
+ { "25S03", "Transaction is rolled back" },
+ { "28000", "Invalid authorization specification" },
+ { "34000", "Invalid cursor name" },
+ { "3C000", "Duplicate cursor name" },
+ { "3D000", "Invalid catalog name" },
+ { "3F000", "Invalid schema name" },
+ { "40001", "Serialization failure" },
+ { "40003", "Statement completion unknown" },
+ { "42000", "Syntax error or access violation" },
+ { "42S01", "Base table or view already exists" },
+ { "42S02", "Base table or view not found" },
+ { "42S11", "Index already exists" },
+ { "42S12", "Index not found" },
+ { "42S21", "Column already exists" },
+ { "42S22", "Column not found" },
+ { "44000", "WITH CHECK OPTION violation" },
+ { "HY000", "General error" },
+ { "HY001", "Memory allocation error" },
+ { "HY003", "Invalid application buffer type" },
+ { "HY004", "Invalid SQL data type" },
+ { "HY007", "Associated statement is not prepared" },
+ { "HY008", "Operation canceled" },
+ { "HY009", "Invalid use of null pointer" },
+ { "HY010", "Function sequence error" },
+ { "HY011", "Attribute cannot be set now" },
+ { "HY012", "Invalid transaction operation code" },
+ { "HY013", "Memory management error" },
+ { "HY014", "Limit on the number of handles exceeded" },
+ { "HY015", "No cursor name available" },
+ { "HY016", "Cannot modify an implementation row descriptor" },
+ { "HY017", "Invalid use of an automatically allocated descriptor handle" },
+ { "HY018", "Server declined cancel request" },
+ { "HY019", "Non-character and non-binary data sent in pieces" },
+ { "HY020", "Attempt to concatenate a null value" },
+ { "HY021", "Inconsistent descriptor information" },
+ { "HY024", "Invalid attribute value" },
+ { "HY090", "Invalid string or buffer length" },
+ { "HY091", "Invalid descriptor field identifier" },
+ { "HY092", "Invalid attribute/option identifier" },
+ { "HY093", "Invalid parameter number" },
+ { "HY095", "Function type out of range" },
+ { "HY096", "Invalid information type" },
+ { "HY097", "Column type out of range" },
+ { "HY098", "Scope type out of range" },
+ { "HY099", "Nullable type out of range" },
+ { "HY100", "Uniqueness option type out of range" },
+ { "HY101", "Accuracy option type out of range" },
+ { "HY103", "Invalid retrieval code" },
+ { "HY104", "Invalid precision or scale value" },
+ { "HY105", "Invalid parameter type" },
+ { "HY106", "Fetch type out of range" },
+ { "HY107", "Row value out of range" },
+ { "HY109", "Invalid cursor position" },
+ { "HY110", "Invalid driver completion" },
+ { "HY111", "Invalid bookmark value" },
+ { "HYC00", "Optional feature not implemented" },
+ { "HYT00", "Timeout expired" },
+ { "HYT01", "Connection timeout expired" },
+ { "IM001", "Driver does not support this function" },
+ { "IM002", "Data source name not found and no default driver specified" },
+ { "IM003", "Specified driver could not be loaded" },
+ { "IM004", "Driver’s SQLAllocHandle on SQL_HANDLE_ENV failed" },
+ { "IM005", "Driver’s SQLAllocHandle on SQL_HANDLE_DBC failed" },
+ { "IM006", "Driver’s SQLSetConnectAttr failed" },
+ { "IM007", "No data source or driver specified; dialog prohibited" },
+ { "IM008", "Dialog failed" },
+ { "IM009", "Unable to load translation DLL" },
+ { "IM010", "Data source name too long" },
+ { "IM011", "Driver name too long" },
+ { "IM012", "DRIVER keyword syntax error" },
+ { "IM013", "Trace file error" },
+ { "IM014", "Invalid name of File DSN" },
+ { "IM015", "Corrupt file data source" }
+};
+
+void pdo_sqlstate_fini_error_table(void)
+{
+ zend_hash_destroy(&err_hash);
+}
+
+int pdo_sqlstate_init_error_table(void)
+{
+ int i;
+ struct pdo_sqlstate_info *info;
+
+ if (FAILURE == zend_hash_init(&err_hash,
+ sizeof(err_initializer)/sizeof(err_initializer[0]), NULL, NULL, 1)) {
+ return FAILURE;
+ }
+
+ for (i = 0; i < sizeof(err_initializer)/sizeof(err_initializer[0]); i++) {
+ info = &err_initializer[i];
+
+ zend_hash_add(&err_hash, info->state, sizeof(info->state), &info, sizeof(info), NULL);
+ }
+
+ return SUCCESS;
+}
+
+const char *pdo_sqlstate_state_to_description(char *state)
+{
+ struct pdo_sqlstate_info **info;
+ if (SUCCESS == zend_hash_find(&err_hash, state, sizeof(err_initializer[0].state),
+ (void**)&info)) {
+ return (*info)->desc;
+ }
+ return NULL;
+}
+
RETURN_FALSE;
}
- RETURN_LONG(stmt->error_code);
+ RETURN_STRING(stmt->error_code, 1);
}
/* }}} */
}
array_init(return_value);
- add_next_index_long(return_value, stmt->error_code);
+ add_next_index_string(return_value, stmt->error_code, 1);
if (stmt->dbh->methods->fetch_err) {
stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value TSRMLS_CC);
#endif
char *method_name, int method_len TSRMLS_DC)
{
- zend_function *fbc;
+ zend_function *fbc = NULL;
char *lc_method_name;
#if PHP_API_VERSION >= 20041225
zval *object = *object_pp;
lc_method_name = emalloc(method_len + 1);
zend_str_tolower_copy(lc_method_name, method_name, method_len);
- if (zend_hash_find(&pdo_dbstmt_ce->function_table, lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
- efree(lc_method_name);
- return NULL;
+ if (zend_hash_find(&pdo_dbstmt_ce->function_table, lc_method_name,
+ method_len+1, (void**)&fbc) == FAILURE) {
+ pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(object TSRMLS_CC);
+ /* not a pre-defined method, nor a user-defined method; check
+ * the driver specific methods */
+ if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
+ if (!pdo_hash_methods(stmt->dbh, PDO_DBH_DRIVER_METHOD_KIND_STMT TSRMLS_CC)) {
+ goto out;
+ }
+ }
+
+ if (zend_hash_find(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH],
+ lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
+ fbc = NULL;
+ goto out;
+ }
+ /* got it */
}
+out:
efree(lc_method_name);
return fbc;
}
# define FALSE 0
#endif
-#define PDO_DRIVER_API 20041027
+#define PDO_DRIVER_API 20050105
enum pdo_param_type {
PDO_PARAM_NULL,
PDO_CURSOR_SCROLL, /* scrollable cursor */
};
+/* SQL-92 SQLSTATE error codes.
+
+The character string value returned for an SQLSTATE consists of a two-character
+class value followed by a three-character subclass value. A class value of 01
+indicates a warning and is accompanied by a return code of
+SQL_SUCCESS_WITH_INFO.
+
+Class values other than '01', except for the class 'IM',
+indicate an error and are accompanied by a return code of SQL_ERROR. The class
+'IM' is specific to warnings and errors that derive from the implementation of
+ODBC itself.
+
+The subclass value '000' in any class indicates that there is no
+subclass for that SQLSTATE. The assignment of class and subclass values is
+defined by SQL-92.
+*/
+
+typedef char pdo_error_type[6]; /* SQLSTATE */
+
+
+#define PDO_ERR_NONE "00000"
+#if 0
/* generic error code values.
* Don't want to go overboard with these.
* */
-enum pdo_error_type {
- PDO_ERR_NONE, /* no error condition */
- PDO_ERR_CANT_MAP, /* no way to map native error to the generic codes; consult the native error for more info */
- PDO_ERR_SYNTAX,
- PDO_ERR_CONSTRAINT,
- PDO_ERR_NOT_FOUND,
- PDO_ERR_ALREADY_EXISTS,
- PDO_ERR_NOT_IMPLEMENTED,
- PDO_ERR_MISMATCH,
- PDO_ERR_TRUNCATED,
- PDO_ERR_DISCONNECTED,
- PDO_ERR_NO_PERM,
+#define PDO_ERR_SYNTAX "42000"
+#define PDO_ERR_CONSTRAINT "23000"
+#define PDO_ERR_NOT_FOUND ""
+#define PDO_ERR_ALREADY_EXISTS,
+#define PDO_ERR_NOT_IMPLEMENTED,
+#define PDO_ERR_MISMATCH,
+#define PDO_ERR_TRUNCATED,
+#define PDO_ERR_DISCONNECTED,
+#define PDO_ERR_NO_PERM,
+
+ PDO_ERR_CANT_MAP, /* no way to map native error to the generic
+ * codes; consult the native error for more info */
};
+#endif
enum pdo_error_mode {
PDO_ERRMODE_SILENT, /* just set error codes */
* You may set this handler to NULL, which is equivalent to returning SUCCESS. */
typedef int (*pdo_dbh_check_liveness_func)(pdo_dbh_t *dbh TSRMLS_DC);
+/* for adding methods to the dbh or stmt objects
+pointer to a list of driver specific functions. The convention is
+to prefix the function names using the PDO driver name; this will
+reduce the chance of collisions with future functionality in the
+PDO class or in user code (they can extend the PDO object).
+*/
+enum {
+ PDO_DBH_DRIVER_METHOD_KIND_DBH = 0,
+ PDO_DBH_DRIVER_METHOD_KIND_STMT,
+ PDO_DBH_DRIVER_METHOD_KIND__MAX
+};
+
+typedef function_entry *(*pdo_dbh_get_driver_methods_func)(pdo_dbh_t *dbh, int kind TSRMLS_DC);
+
struct pdo_dbh_methods {
pdo_dbh_close_func closer;
pdo_dbh_prepare_func preparer;
pdo_dbh_fetch_error_func fetch_err;
pdo_dbh_get_attr_func get_attribute;
pdo_dbh_check_liveness_func check_liveness;
+ pdo_dbh_get_driver_methods_func get_driver_methods;
};
/* }}} */
unsigned long data_source_len;
/* the global error code. */
- enum pdo_error_type error_code;
+ pdo_error_type error_code;
enum pdo_error_mode error_mode;
int persistent_id_len;
unsigned int refcount;
+ /* driver specific "class" methods for the dbh and stmt */
+ HashTable *cls_methods[PDO_DBH_DRIVER_METHOD_KIND__MAX-1];
};
/* describes a column */
int active_query_stringlen;
/* the cursor specific error code. */
- enum pdo_error_type error_code;
+ pdo_error_type error_code;
/* for lazy fetches, we always return the same lazy object handle.
* Let's keep it here. */
extern void pdo_handle_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt TSRMLS_DC);
-#define PDO_DBH_CLEAR_ERR() dbh->error_code = PDO_ERR_NONE
-#define PDO_STMT_CLEAR_ERR() stmt->error_code = PDO_ERR_NONE
+#define PDO_DBH_CLEAR_ERR() strcpy(dbh->error_code, PDO_ERR_NONE)
+#define PDO_STMT_CLEAR_ERR() strcpy(stmt->error_code, PDO_ERR_NONE)
#define PDO_HANDLE_DBH_ERR() if (dbh->error_code) { pdo_handle_error(dbh, NULL TSRMLS_CC); }
#define PDO_HANDLE_STMT_ERR() if (stmt->error_code) { pdo_handle_error(stmt->dbh, stmt TSRMLS_CC); }
+int pdo_sqlstate_init_error_table(void);
+void pdo_sqlstate_fini_error_table(void);
+const char *pdo_sqlstate_state_to_description(char *state);
+int pdo_hash_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC);
+
/*
* Local variables: