Skip to content

Commit 8d9c384

Browse files
committed
Fix doer()
Implement error handling for ODBC
1 parent b4ef3d1 commit 8d9c384

File tree

4 files changed

+151
-31
lines changed

4 files changed

+151
-31
lines changed

ext/pdo_odbc/odbc_driver.c

Lines changed: 130 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,113 @@
3030
#include "php_pdo_odbc.h"
3131
#include "php_pdo_odbc_int.h"
3232

33-
void _odbc_error(pdo_dbh_t *dbh, char *what, PDO_ODBC_HSTMT stmt, const char *file, int line TSRMLS_DC) /* {{{ */
33+
static struct {
34+
char state[6];
35+
enum pdo_error err;
36+
} odbc_to_pdo_err_map[] = {
37+
/* this table maps ODBC V3 SQLSTATE codes to PDO_ERR codes */
38+
{ "42S01", PDO_ERR_ALREADY_EXISTS },
39+
{ "42S11", PDO_ERR_ALREADY_EXISTS },
40+
{ "42S21", PDO_ERR_ALREADY_EXISTS },
41+
{ "IM001", PDO_ERR_NOT_IMPLEMENTED },
42+
{ "42S22", PDO_ERR_NOT_FOUND },
43+
{ "42S12", PDO_ERR_NOT_FOUND },
44+
{ "42S02", PDO_ERR_NOT_FOUND },
45+
{ "42000", PDO_ERR_SYNTAX },
46+
{ "23000", PDO_ERR_CONSTRAINT },
47+
{ "22025", PDO_ERR_SYNTAX },
48+
{ "22019", PDO_ERR_SYNTAX },
49+
{ "22018", PDO_ERR_SYNTAX },
50+
{ "08S01", PDO_ERR_DISCONNECTED },
51+
{ "01S07", PDO_ERR_TRUNCATED },
52+
53+
};
54+
55+
static HashTable err_hash;
56+
57+
void pdo_odbc_fini_error_table(void)
58+
{
59+
zend_hash_destroy(&err_hash);
60+
}
61+
62+
void pdo_odbc_init_error_table(void)
63+
{
64+
int i;
65+
66+
zend_hash_init(&err_hash, 0, NULL, NULL, 1);
67+
68+
for (i = 0; i < sizeof(odbc_to_pdo_err_map)/sizeof(odbc_to_pdo_err_map[0]); i++) {
69+
zend_hash_add(&err_hash, odbc_to_pdo_err_map[i].state, sizeof(odbc_to_pdo_err_map[i].state),
70+
&odbc_to_pdo_err_map[i].err, sizeof(odbc_to_pdo_err_map[i].err), NULL);
71+
}
72+
}
73+
74+
static enum pdo_error pdo_odbc_map_error(char *state)
75+
{
76+
enum pdo_error *p_err;
77+
if (SUCCESS == zend_hash_find(&err_hash, state, sizeof(odbc_to_pdo_err_map[0].state), (void**)&p_err)) {
78+
return *p_err;
79+
}
80+
return PDO_ERR_CANT_MAP;
81+
}
82+
83+
static int pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC)
84+
{
85+
pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
86+
pdo_odbc_errinfo *einfo = &H->einfo;
87+
pdo_odbc_stmt *S = NULL;
88+
char *message = NULL;
89+
90+
if (stmt) {
91+
S = (pdo_odbc_stmt*)stmt->driver_data;
92+
einfo = &S->einfo;
93+
}
94+
95+
spprintf(&message, 0, "%s: %d %s [SQL State %s] (%s:%d)",
96+
einfo->what, einfo->last_error, einfo->last_err_msg, einfo->last_state, einfo->file, einfo->line);
97+
98+
add_next_index_long(info, einfo->last_error);
99+
add_next_index_string(info, message, 0);
100+
add_next_index_string(info, einfo->last_state, 1);
101+
102+
return 1;
103+
}
104+
105+
106+
void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line TSRMLS_DC) /* {{{ */
34107
{
35108
RETCODE rc;
36109
SWORD errmsgsize;
37110
pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
111+
pdo_odbc_errinfo *einfo = &H->einfo;
112+
pdo_odbc_stmt *S = NULL;
113+
enum pdo_error *pdo_err = &dbh->error_code;
114+
115+
if (stmt) {
116+
S = (pdo_odbc_stmt*)stmt->driver_data;
38117

39-
rc = SQLError(H->env, H->dbc, stmt, H->last_state, &H->last_error,
40-
H->last_err_msg, sizeof(H->last_err_msg)-1, &errmsgsize);
118+
einfo = &S->einfo;
119+
pdo_err = &stmt->error_code;
120+
}
41121

42-
H->last_err_msg[errmsgsize] = '\0';
122+
if (statement == SQL_NULL_HSTMT && S) {
123+
statement = S->stmt;
124+
}
43125

44-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: %d %s [SQL State %s]",
45-
file, line, what, H->last_error, H->last_err_msg, H->last_state);
126+
rc = SQLError(H->env, H->dbc, statement, einfo->last_state, &einfo->last_error,
127+
einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize);
128+
129+
einfo->last_err_msg[errmsgsize] = '\0';
130+
einfo->file = file;
131+
einfo->line = line;
132+
einfo->what = what;
133+
134+
*pdo_err = pdo_odbc_map_error(einfo->last_state);
135+
136+
if (!dbh->methods) {
137+
zend_throw_exception_ex(php_pdo_get_exception(), *pdo_err TSRMLS_CC, "%s: %d %s [SQL State %s]",
138+
what, einfo->last_error, einfo->last_err_msg, einfo->last_state);
139+
}
46140
}
47141
/* }}} */
48142

@@ -54,7 +148,7 @@ static int odbc_handle_closer(pdo_dbh_t *dbh TSRMLS_DC)
54148
SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
55149
}
56150

57-
SQLFreeHandle(SQL_HANDLE_STMT, H->dbc);
151+
SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);
58152
SQLFreeHandle(SQL_HANDLE_ENV, H->env);
59153
pefree(H, dbh->is_persistent);
60154

@@ -73,14 +167,14 @@ static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, p
73167

74168
if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {
75169
efree(S);
76-
odbc_error(dbh, "SQLAllocStmt", SQL_NULL_HSTMT);
170+
pdo_odbc_drv_error("SQLAllocStmt");
77171
return 0;
78172
}
79173

80174
rc = SQLPrepare(S->stmt, (char*)sql, SQL_NTS);
81175

82176
if (rc != SQL_SUCCESS) {
83-
odbc_error(dbh, "SQLPrepare", S->stmt);
177+
pdo_odbc_stmt_error("SQLPrepare");
84178
}
85179

86180
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
@@ -98,20 +192,29 @@ static long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRML
98192
{
99193
pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
100194
RETCODE rc;
101-
long row_count;
195+
long row_count = -1;
196+
PDO_ODBC_HSTMT stmt;
197+
198+
rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);
199+
if (rc != SQL_SUCCESS) {
200+
pdo_odbc_drv_error("SQLAllocHandle: STMT");
201+
return -1;
202+
}
203+
204+
rc = SQLExecDirect(stmt, (char *)sql, sql_len);
102205

103-
rc = SQLExecDirect(H->dbc, (char *)sql, sql_len);
104206
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
105-
/* XXX error reporting needed */
106-
return 0;
207+
pdo_odbc_doer_error("SQLExecDirect");
208+
goto out;
107209
}
108210

109-
rc = SQLRowCount(H->dbc, &row_count);
211+
rc = SQLRowCount(stmt, &row_count);
110212
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
111-
/* XXX error reporting needed */
112-
return 0;
213+
pdo_odbc_doer_error("SQLRowCount");
214+
goto out;
113215
}
114-
216+
out:
217+
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
115218
return row_count;
116219
}
117220

@@ -136,7 +239,7 @@ static int odbc_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
136239
rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);
137240

138241
if (rc != SQL_SUCCESS) {
139-
odbc_error(dbh, "SQLEndTran: Commit", SQL_NULL_HSTMT);
242+
pdo_odbc_drv_error("SQLEndTran: Commit");
140243

141244
if (rc != SQL_SUCCESS_WITH_INFO) {
142245
return 0;
@@ -153,7 +256,7 @@ static int odbc_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
153256
rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
154257

155258
if (rc != SQL_SUCCESS) {
156-
odbc_error(dbh, "SQLEndTran: Rollback", SQL_NULL_HSTMT);
259+
pdo_odbc_drv_error("SQLEndTran: Rollback");
157260

158261
if (rc != SQL_SUCCESS_WITH_INFO) {
159262
return 0;
@@ -173,6 +276,10 @@ static struct pdo_dbh_methods odbc_methods = {
173276
odbc_handle_begin,
174277
odbc_handle_commit,
175278
odbc_handle_rollback,
279+
NULL, /* set attr */
280+
NULL, /* last id */
281+
pdo_odbc_fetch_error_func,
282+
NULL, /* get attr */
176283
};
177284

178285
static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
@@ -189,22 +296,22 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_D
189296
rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
190297

191298
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
192-
odbc_error(dbh, "SQLSetEnvAttr: ODBC3", SQL_NULL_HSTMT);
299+
pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3");
193300
odbc_handle_closer(dbh TSRMLS_CC);
194301
return 0;
195302
}
196303

197304
rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);
198305
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
199-
odbc_error(dbh, "SQLAllocHandle (DBC)", SQL_NULL_HSTMT);
306+
pdo_odbc_drv_error("SQLAllocHandle (DBC)");
200307
odbc_handle_closer(dbh TSRMLS_CC);
201308
return 0;
202309
}
203310

204311
if (!dbh->auto_commit) {
205312
rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
206313
if (rc != SQL_SUCCESS) {
207-
odbc_error(dbh, "SQLSetConnectAttr AUTOCOMMIT = OFF", SQL_NULL_HSTMT);
314+
pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF");
208315
odbc_handle_closer(dbh TSRMLS_CC);
209316
return 0;
210317
}
@@ -232,7 +339,7 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_D
232339
}
233340

234341
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
235-
odbc_error(dbh, use_direct ? "SQLDriverConnect" : "SQLConnect", SQL_NULL_HSTMT);
342+
pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect");
236343
odbc_handle_closer(dbh TSRMLS_CC);
237344
return 0;
238345
}

ext/pdo_odbc/odbc_stmt.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@ static int odbc_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
7373
break;
7474
case SQL_NO_DATA_FOUND:
7575
case SQL_SUCCESS_WITH_INFO:
76-
odbc_error(stmt->dbh, "SQLExecute", S->stmt);
76+
pdo_odbc_stmt_error("SQLExecute");
7777
break;
7878

7979
default:
80-
odbc_error(stmt->dbh, "SQLExecute", S->stmt);
80+
pdo_odbc_stmt_error("SQLExecute");
8181
return 0;
8282
}
8383

@@ -156,7 +156,7 @@ static int odbc_stmt_fetch(pdo_stmt_t *stmt TSRMLS_DC)
156156
return 0;
157157
}
158158

159-
odbc_error(stmt->dbh, "SQLFetch", S->stmt);
159+
pdo_odbc_stmt_error("SQLFetch");
160160

161161
return 0;
162162
}

ext/pdo_odbc/pdo_odbc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ ZEND_GET_MODULE(pdo_odbc)
6868
PHP_MINIT_FUNCTION(pdo_odbc)
6969
{
7070
php_pdo_register_driver(&pdo_odbc_driver);
71+
pdo_odbc_init_error_table();
7172
return SUCCESS;
7273
}
7374
/* }}} */
@@ -77,6 +78,7 @@ PHP_MINIT_FUNCTION(pdo_odbc)
7778
PHP_MSHUTDOWN_FUNCTION(pdo_odbc)
7879
{
7980
php_pdo_unregister_driver(&pdo_odbc_driver);
81+
pdo_odbc_fini_error_table();
8082
return SUCCESS;
8183
}
8284
/* }}} */

ext/pdo_odbc/php_pdo_odbc_int.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,17 @@
105105
/* }}} */
106106

107107
typedef struct {
108-
PDO_ODBC_HENV env;
109-
PDO_ODBC_HDBC dbc;
110-
111108
char last_state[6];
112109
char last_err_msg[SQL_MAX_MESSAGE_LENGTH];
113110
SDWORD last_error;
111+
const char *file, *what;
112+
int line;
113+
} pdo_odbc_errinfo;
114+
115+
typedef struct {
116+
PDO_ODBC_HENV env;
117+
PDO_ODBC_HDBC dbc;
118+
pdo_odbc_errinfo einfo;
114119
} pdo_odbc_db_handle;
115120

116121
typedef struct {
@@ -125,13 +130,19 @@ typedef struct {
125130
PDO_ODBC_HSTMT stmt;
126131
pdo_odbc_column *cols;
127132
pdo_odbc_db_handle *H;
133+
pdo_odbc_errinfo einfo;
128134
} pdo_odbc_stmt;
129135

130136
extern pdo_driver_t pdo_odbc_driver;
131137
extern struct pdo_stmt_methods odbc_stmt_methods;
132138

133-
void _odbc_error(pdo_dbh_t *dbh, char *what, PDO_ODBC_HSTMT stmt, const char *file, int line TSRMLS_DC);
134-
#define odbc_error(dbh, what, stmt) _odbc_error(dbh, what, stmt, __FILE__, __LINE__ TSRMLS_CC)
139+
void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line TSRMLS_DC);
140+
#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__ TSRMLS_CC)
141+
#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__ TSRMLS_CC)
142+
#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__ TSRMLS_CC)
143+
144+
void pdo_odbc_init_error_table(void);
145+
void pdo_odbc_fini_error_table(void);
135146

136147
/*
137148
* Local variables:

0 commit comments

Comments
 (0)