summaryrefslogtreecommitdiff
path: root/ext/pdo_dblib
diff options
context:
space:
mode:
authorJakub Zelenka <bukka@php.net>2016-06-19 17:05:48 +0100
committerJakub Zelenka <bukka@php.net>2016-06-19 17:05:48 +0100
commite63a8540a60e95aa5bd8e269add1b02afcc1b79b (patch)
treeb83a144eec24cc81adab0b9a778f7a730d8df79e /ext/pdo_dblib
parent7a4cc73641bb3eb878f7184bcbd026ee663cf2a9 (diff)
parent53071e647049f099f7f7a0771ddb63fc2cdd621c (diff)
downloadphp-git-e63a8540a60e95aa5bd8e269add1b02afcc1b79b.tar.gz
Merge branch 'openssl_error_store' into openssl_aead
Diffstat (limited to 'ext/pdo_dblib')
-rw-r--r--ext/pdo_dblib/dblib_driver.c19
-rw-r--r--ext/pdo_dblib/dblib_stmt.c272
-rw-r--r--ext/pdo_dblib/pdo_dblib.c3
-rw-r--r--ext/pdo_dblib/php_pdo_dblib_int.h8
-rw-r--r--ext/pdo_dblib/tests/bug_38955.phpt8
-rw-r--r--ext/pdo_dblib/tests/bug_45876.phpt8
-rw-r--r--ext/pdo_dblib/tests/bug_47588.phpt6
-rw-r--r--ext/pdo_dblib/tests/bug_68957.phpt2
-rw-r--r--ext/pdo_dblib/tests/bug_69757.phpt4
-rw-r--r--ext/pdo_dblib/tests/bug_71667.phpt34
-rw-r--r--ext/pdo_dblib/tests/pdo_dblib_quote.phpt24
-rw-r--r--ext/pdo_dblib/tests/timeout.phpt51
-rw-r--r--ext/pdo_dblib/tests/types.phpt66
13 files changed, 414 insertions, 91 deletions
diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c
index dcbaf55a3f..ad54ae57b3 100644
--- a/ext/pdo_dblib/dblib_driver.c
+++ b/ext/pdo_dblib/dblib_driver.c
@@ -101,6 +101,7 @@ static int dblib_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len
stmt->driver_data = S;
stmt->methods = &dblib_stmt_methods;
stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
+ S->computed_column_name_count = 0;
S->err.sqlstate = stmt->error_code;
return 1;
@@ -170,7 +171,7 @@ static int dblib_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unqu
*
*/
*quotedlen = (unquotedlen * 2) + 2; /* 2 chars per byte +2 for "0x" prefix */
- q = *quoted = emalloc(*quotedlen);
+ q = *quoted = emalloc(*quotedlen+1); /* Add byte for terminal null */
*q++ = '0';
*q++ = 'x';
@@ -181,7 +182,7 @@ static int dblib_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unqu
} else {
/* Alpha/Numeric Quoting */
*quotedlen += 2; /* +2 for opening, closing quotes */
- q = *quoted = emalloc(*quotedlen);
+ q = *quoted = emalloc(*quotedlen+1); /* Add byte for terminal null */
*q++ = '\'';
for (i=0;i<unquotedlen;i++) {
@@ -347,9 +348,19 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, nvars);
if (driver_options) {
+ int connect_timeout = pdo_attr_lval(driver_options, PDO_DBLIB_ATTR_CONNECTION_TIMEOUT, -1);
+ int query_timeout = pdo_attr_lval(driver_options, PDO_DBLIB_ATTR_QUERY_TIMEOUT, -1);
int timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);
- dbsetlogintime(timeout); /* Connection/Login Timeout */
- dbsettime(timeout); /* Statement Timeout */
+
+ if (connect_timeout == -1) {
+ connect_timeout = timeout;
+ }
+ if (query_timeout == -1) {
+ query_timeout = timeout;
+ }
+
+ dbsetlogintime(connect_timeout); /* Connection/Login Timeout */
+ dbsettime(query_timeout); /* Statement Timeout */
}
H = pecalloc(1, sizeof(*H), dbh->is_persistent);
diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c
index 0eff4945dd..a1055434d3 100644
--- a/ext/pdo_dblib/dblib_stmt.c
+++ b/ext/pdo_dblib/dblib_stmt.c
@@ -95,6 +95,22 @@ static char *pdo_dblib_get_field_name(int type)
}
/* }}} */
+static void pdo_dblib_err_dtor(pdo_dblib_err *err)
+{
+ if (err->dberrstr) {
+ efree(err->dberrstr);
+ err->dberrstr = NULL;
+ }
+ if (err->lastmsg) {
+ efree(err->lastmsg);
+ err->lastmsg = NULL;
+ }
+ if (err->oserrstr) {
+ efree(err->oserrstr);
+ err->oserrstr = NULL;
+ }
+}
+
static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt)
{
pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
@@ -102,6 +118,8 @@ static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt)
/* Cancel any pending results */
dbcancel(H->link);
+
+ pdo_dblib_err_dtor(&H->err);
return 1;
}
@@ -110,6 +128,8 @@ static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt)
{
pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
+ pdo_dblib_err_dtor(&S->err);
+
efree(S);
return 1;
@@ -198,21 +218,31 @@ static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno)
return FAILURE;
}
+ if (colno == 0) {
+ S->computed_column_name_count = 0;
+ }
+
col = &stmt->columns[colno];
fname = (char*)dbcolname(H->link, colno+1);
if (fname && *fname) {
col->name = zend_string_init(fname, strlen(fname), 0);
} else {
- char buf[16];
- int len;
-
- len = snprintf(buf, sizeof(buf), "computed%d", colno);
- col->name = zend_string_init(buf, len, 0);
+ if (S->computed_column_name_count > 0) {
+ char buf[16];
+ int len;
+
+ len = snprintf(buf, sizeof(buf), "computed%d", S->computed_column_name_count);
+ col->name = zend_string_init(buf, len, 0);
+ } else {
+ col->name = zend_string_init("computed", strlen("computed"), 0);
+ }
+
+ S->computed_column_name_count++;
}
col->maxlen = dbcollen(H->link, colno+1);
- col->param_type = PDO_PARAM_STR;
+ col->param_type = PDO_PARAM_ZVAL;
return 1;
}
@@ -225,78 +255,161 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
pdo_dblib_db_handle *H = S->H;
int coltype;
- unsigned int tmp_len;
- char *tmp_ptr = NULL;
+ char *data, *tmp_data;
+ unsigned int data_len, tmp_data_len;
+ zval *zv = NULL;
coltype = dbcoltype(H->link, colno+1);
-
- *len = dbdatlen(H->link, colno+1);
- *ptr = dbdata(H->link, colno+1);
-
- if (*len == 0 && *ptr == NULL) {
- return 1;
- }
-
- switch (coltype) {
- case SQLVARBINARY:
- case SQLBINARY:
- case SQLIMAGE:
- case SQLTEXT:
- /* FIXME: Above types should be returned as a stream as they can be VERY large */
- case SQLCHAR:
- case SQLVARCHAR:
- tmp_ptr = emalloc(*len + 1);
- memcpy(tmp_ptr, *ptr, *len);
- tmp_ptr[*len] = '\0';
- *ptr = tmp_ptr;
- break;
- case SQLMONEY:
- case SQLMONEY4:
- case SQLMONEYN: {
- DBFLT8 money_value;
- dbconvert(NULL, coltype, *ptr, *len, SQLFLT8, (LPBYTE)&money_value, 8);
- *len = spprintf(&tmp_ptr, 0, "%.4f", money_value);
- *ptr = tmp_ptr;
- break;
- }
- case SQLUNIQUE: {
- *len = 37;
- tmp_ptr = emalloc(*len + 1);
- *len = dbconvert(NULL, SQLUNIQUE, *ptr, *len, SQLCHAR, tmp_ptr, *len);
- php_strtoupper(tmp_ptr, *len);
- tmp_ptr[36] = '\0';
- *ptr = tmp_ptr;
- break;
+ data = dbdata(H->link, colno+1);
+ data_len = dbdatlen(H->link, colno+1);
+
+ if (data_len != 0 || data != NULL) {
+ if (stmt->dbh->stringify) {
+ switch (coltype) {
+ case SQLFLT4:
+ case SQLFLT8:
+ case SQLINT4:
+ case SQLINT2:
+ case SQLINT1:
+ case SQLBIT: {
+ if (dbwillconvert(coltype, SQLCHAR)) {
+ tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */
+ tmp_data = emalloc(tmp_data_len);
+ data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, tmp_data, -1);
+
+ zv = emalloc(sizeof(zval));
+ ZVAL_STRING(zv, tmp_data);
+
+ efree(tmp_data);
+ }
+ break;
+ }
+ }
}
- case SQLDATETIM4:
- case SQLDATETIME: {
- DBDATETIME dt;
- DBDATEREC di;
-
- dbconvert(H->link, coltype, (BYTE*) *ptr, -1, SQLDATETIME, (LPBYTE) &dt, -1);
- dbdatecrack(H->link, &di, &dt);
- *len = spprintf((char**) &tmp_ptr, 20, "%d-%02d-%02d %02d:%02d:%02d",
-#ifdef PHP_DBLIB_IS_MSSQL || MSDBLIB
- di.year, di.month, di.day, di.hour, di.minute, di.second
+ if (!zv) {
+ switch (coltype) {
+ case SQLCHAR:
+ case SQLVARCHAR:
+ case SQLTEXT: {
+#if ilia_0
+ while (data_len>0 && data[data_len-1] == ' ') { /* nuke trailing whitespace */
+ data_len--;
+ }
+#endif
+ }
+ case SQLVARBINARY:
+ case SQLBINARY:
+ case SQLIMAGE: {
+ zv = emalloc(sizeof(zval));
+ ZVAL_STRINGL(zv, data, data_len);
+
+ break;
+ }
+ case SQLDATETIME:
+ case SQLDATETIM4: {
+ int dl;
+ DBDATEREC di;
+ DBDATEREC dt;
+
+ dbconvert(H->link, coltype, data, -1, SQLDATETIME, (LPBYTE) &dt, -1);
+ dbdatecrack(H->link, &di, (DBDATETIME *) &dt);
+
+ dl = spprintf(&tmp_data, 20, "%d-%02d-%02d %02d:%02d:%02d",
+#if defined(PHP_DBLIB_IS_MSSQL) || defined(MSDBLIB)
+ di.year, di.month, di.day, di.hour, di.minute, di.second
+#else
+ di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
+#endif
+ );
+
+ zv = emalloc(sizeof(zval));
+ ZVAL_STRINGL(zv, tmp_data, dl);
+
+ efree(tmp_data);
+
+ break;
+ }
+ case SQLFLT4: {
+ zv = emalloc(sizeof(zval));
+ ZVAL_DOUBLE(zv, (double) (*(DBFLT4 *) data));
+
+ break;
+ }
+ case SQLFLT8: {
+ zv = emalloc(sizeof(zval));
+ ZVAL_DOUBLE(zv, (double) (*(DBFLT8 *) data));
+
+ break;
+ }
+ case SQLINT4: {
+ zv = emalloc(sizeof(zval));
+ ZVAL_LONG(zv, (long) ((int) *(DBINT *) data));
+
+ break;
+ }
+ case SQLINT2: {
+ zv = emalloc(sizeof(zval));
+ ZVAL_LONG(zv, (long) ((int) *(DBSMALLINT *) data));
+
+ break;
+ }
+ case SQLINT1:
+ case SQLBIT: {
+ zv = emalloc(sizeof(zval));
+ ZVAL_LONG(zv, (long) ((int) *(DBTINYINT *) data));
+
+ break;
+ }
+ case SQLMONEY:
+ case SQLMONEY4:
+ case SQLMONEYN: {
+ DBFLT8 money_value;
+ dbconvert(NULL, coltype, data, 8, SQLFLT8, (LPBYTE)&money_value, -1);
+
+ zv = emalloc(sizeof(zval));
+ ZVAL_DOUBLE(zv, money_value);
+
+ if (stmt->dbh->stringify) {
+ convert_to_string(zv);
+ }
+
+ break;
+ }
+#ifdef SQLUNIQUE
+ case SQLUNIQUE: {
#else
- di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
+ case 36: { /* FreeTDS hack */
#endif
- );
+ zv = emalloc(sizeof(zval));
+ ZVAL_STRINGL(zv, data, 16); /* uniqueidentifier is a 16-byte binary number */
- *ptr = (char*) tmp_ptr;
- break;
- }
- default:
- if (dbwillconvert(coltype, SQLCHAR)) {
- tmp_len = 32 + (2 * (*len)); /* FIXME: We allocate more than we need here */
- tmp_ptr = emalloc(tmp_len);
- *len = dbconvert(NULL, coltype, *ptr, *len, SQLCHAR, tmp_ptr, -1);
- *ptr = tmp_ptr;
- } else {
- *len = 0; /* FIXME: Silently fails and returns null on conversion errors */
- *ptr = NULL;
+ break;
+ }
+ default: {
+ if (dbwillconvert(coltype, SQLCHAR)) {
+ tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */
+ tmp_data = emalloc(tmp_data_len);
+ data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, tmp_data, -1);
+
+ zv = emalloc(sizeof(zval));
+ ZVAL_STRING(zv, tmp_data);
+
+ efree(tmp_data);
+ }
+
+ break;
+ }
}
+ }
+ }
+
+ if (zv != NULL) {
+ *ptr = (char*)zv;
+ *len = sizeof(zval);
+ } else {
+ *ptr = NULL;
+ *len = 0;
}
*caller_frees = 1;
@@ -315,6 +428,7 @@ static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zva
pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
pdo_dblib_db_handle *H = S->H;
DBTYPEINFO* dbtypeinfo;
+ int coltype;
if(colno >= stmt->column_count || colno < 0) {
return FAILURE;
@@ -326,14 +440,28 @@ static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zva
if(!dbtypeinfo) return FAILURE;
+ coltype = dbcoltype(H->link, colno+1);
+
add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1));
- add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(dbcoltype(H->link, colno+1)));
- add_assoc_long(return_value, "native_type_id", dbcoltype(H->link, colno+1));
+ add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(coltype));
+ add_assoc_long(return_value, "native_type_id", coltype);
add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
+ switch (coltype) {
+ case SQLBIT:
+ case SQLINT1:
+ case SQLINT2:
+ case SQLINT4:
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT);
+ break;
+ default:
+ add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
+ break;
+ }
+
return 1;
}
diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c
index 0b64bce8a7..dd64cbe882 100644
--- a/ext/pdo_dblib/pdo_dblib.c
+++ b/ext/pdo_dblib/pdo_dblib.c
@@ -171,6 +171,9 @@ PHP_RSHUTDOWN_FUNCTION(pdo_dblib)
PHP_MINIT_FUNCTION(pdo_dblib)
{
+ REGISTER_PDO_CLASS_CONST_LONG("DBLIB_ATTR_CONNECTION_TIMEOUT", (long) PDO_DBLIB_ATTR_CONNECTION_TIMEOUT);
+ REGISTER_PDO_CLASS_CONST_LONG("DBLIB_ATTR_QUERY_TIMEOUT", (long) PDO_DBLIB_ATTR_QUERY_TIMEOUT);
+
if (FAIL == dbinit()) {
return FAILURE;
}
diff --git a/ext/pdo_dblib/php_pdo_dblib_int.h b/ext/pdo_dblib/php_pdo_dblib_int.h
index 5068c83761..01586881d5 100644
--- a/ext/pdo_dblib/php_pdo_dblib_int.h
+++ b/ext/pdo_dblib/php_pdo_dblib_int.h
@@ -118,6 +118,7 @@ typedef struct {
typedef struct {
pdo_dblib_db_handle *H;
pdo_dblib_err err;
+ unsigned int computed_column_name_count;
} pdo_dblib_stmt;
typedef struct {
@@ -137,7 +138,12 @@ ZEND_END_MODULE_GLOBALS(dblib)
# define DBLIB_G(v) (dblib_globals.v)
#endif
-ZEND_EXTERN_MODULE_GLOBALS(dblib);
+ZEND_EXTERN_MODULE_GLOBALS(dblib)
+
+enum {
+ PDO_DBLIB_ATTR_CONNECTION_TIMEOUT = PDO_ATTR_DRIVER_SPECIFIC,
+ PDO_DBLIB_ATTR_QUERY_TIMEOUT
+};
#endif
diff --git a/ext/pdo_dblib/tests/bug_38955.phpt b/ext/pdo_dblib/tests/bug_38955.phpt
index 1954ed460b..57adbf6c40 100644
--- a/ext/pdo_dblib/tests/bug_38955.phpt
+++ b/ext/pdo_dblib/tests/bug_38955.phpt
@@ -37,21 +37,21 @@ array(4) {
[0]=>
array(1) {
["val"]=>
- string(1) "1"
+ int(1)
}
[1]=>
array(1) {
["val"]=>
- string(1) "2"
+ int(2)
}
[2]=>
array(1) {
["val"]=>
- string(1) "3"
+ int(3)
}
[3]=>
array(1) {
["val"]=>
- string(1) "4"
+ int(4)
}
}
diff --git a/ext/pdo_dblib/tests/bug_45876.phpt b/ext/pdo_dblib/tests/bug_45876.phpt
index 5b595519f2..5f1026e623 100644
--- a/ext/pdo_dblib/tests/bug_45876.phpt
+++ b/ext/pdo_dblib/tests/bug_45876.phpt
@@ -14,7 +14,7 @@ $stmt->execute();
var_dump($stmt->getColumnMeta(0));
$stmt = null;
?>
---EXPECT--
+--EXPECTF--
array(10) {
["max_length"]=>
int(255)
@@ -27,13 +27,13 @@ array(10) {
["native_type"]=>
string(4) "char"
["native_type_id"]=>
- int(47)
+ int(%d)
["native_usertype_id"]=>
+ int(%d)
+ ["pdo_type"]=>
int(2)
["name"]=>
string(13) "TABLE_CATALOG"
["len"]=>
int(255)
- ["pdo_type"]=>
- int(2)
}
diff --git a/ext/pdo_dblib/tests/bug_47588.phpt b/ext/pdo_dblib/tests/bug_47588.phpt
index d8f424e872..262720f632 100644
--- a/ext/pdo_dblib/tests/bug_47588.phpt
+++ b/ext/pdo_dblib/tests/bug_47588.phpt
@@ -23,21 +23,21 @@ array(3) {
[0]=>
array(2) {
["My Field"]=>
- string(1) "1"
+ int(1)
["Another Field"]=>
string(11) "test_string"
}
[1]=>
array(2) {
["My Field"]=>
- string(1) "2"
+ int(2)
["Another Field"]=>
string(11) "test_string"
}
[2]=>
array(2) {
["My Field"]=>
- string(1) "3"
+ int(3)
["Another Field"]=>
string(11) "test_string"
}
diff --git a/ext/pdo_dblib/tests/bug_68957.phpt b/ext/pdo_dblib/tests/bug_68957.phpt
index 3d6e2fd13d..47ff461bb3 100644
--- a/ext/pdo_dblib/tests/bug_68957.phpt
+++ b/ext/pdo_dblib/tests/bug_68957.phpt
@@ -21,7 +21,7 @@ Array
(
[0] => Array
(
- [computed0] => 1
+ [computed] => 1
[0] => 1
)
diff --git a/ext/pdo_dblib/tests/bug_69757.phpt b/ext/pdo_dblib/tests/bug_69757.phpt
index 6c4aee0b6d..611f41bec1 100644
--- a/ext/pdo_dblib/tests/bug_69757.phpt
+++ b/ext/pdo_dblib/tests/bug_69757.phpt
@@ -11,8 +11,8 @@ require __DIR__ . '/config.inc';
$sql = "
exec dbo.sp_executesql N'
- SELECT * FROM sysobjects
- SELECT * FROM syscolumns
+ SELECT TOP 1 * FROM sysobjects
+ SELECT TOP 1 * FROM syscolumns
'
";
$stmt = $db->query($sql);
diff --git a/ext/pdo_dblib/tests/bug_71667.phpt b/ext/pdo_dblib/tests/bug_71667.phpt
new file mode 100644
index 0000000000..1c5005fd6a
--- /dev/null
+++ b/ext/pdo_dblib/tests/bug_71667.phpt
@@ -0,0 +1,34 @@
+--TEST--
+PDO_DBLIB: Emulate how mssql extension names "computed" columns
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo_dblib')) die('skip not loaded');
+require dirname(__FILE__) . '/config.inc';
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/config.inc';
+
+$stmt = $db->prepare("SELECT 1, 2 AS named, 3");
+$stmt->execute();
+var_dump($stmt->fetchAll());
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ array(6) {
+ ["computed"]=>
+ string(1) "1"
+ [0]=>
+ string(1) "1"
+ ["named"]=>
+ string(1) "2"
+ [1]=>
+ string(1) "2"
+ ["computed1"]=>
+ string(1) "3"
+ [2]=>
+ string(1) "3"
+ }
+}
diff --git a/ext/pdo_dblib/tests/pdo_dblib_quote.phpt b/ext/pdo_dblib/tests/pdo_dblib_quote.phpt
new file mode 100644
index 0000000000..24a36dec0b
--- /dev/null
+++ b/ext/pdo_dblib/tests/pdo_dblib_quote.phpt
@@ -0,0 +1,24 @@
+--TEST--
+PDO_DBLIB: Ensure quote function returns expected results
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo_dblib')) die('skip not loaded');
+require dirname(__FILE__) . '/config.inc';
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/config.inc';
+var_dump($db->quote(true, PDO::PARAM_BOOL));
+var_dump($db->quote(false, PDO::PARAM_BOOL));
+var_dump($db->quote(42, PDO::PARAM_INT));
+var_dump($db->quote(null, PDO::PARAM_NULL));
+var_dump($db->quote('\'', PDO::PARAM_STR));
+var_dump($db->quote('foo', PDO::PARAM_STR));
+?>
+--EXPECT--
+string(3) "'1'"
+string(2) "''"
+string(4) "'42'"
+string(2) "''"
+string(4) "''''"
+string(5) "'foo'"
diff --git a/ext/pdo_dblib/tests/timeout.phpt b/ext/pdo_dblib/tests/timeout.phpt
new file mode 100644
index 0000000000..f92a7da72f
--- /dev/null
+++ b/ext/pdo_dblib/tests/timeout.phpt
@@ -0,0 +1,51 @@
+--TEST--
+PDO_DBLIB: Set query timeouts
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo_dblib')) die('skip not loaded');
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+require dirname(__FILE__) . '/config.inc';
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/config.inc';
+
+$sql = 'WAITFOR DELAY \'00:00:02\'';
+
+// querying without a timeout will succeed
+$stmt = $db->prepare($sql);
+if ($stmt->execute()) {
+ echo "OK\n";
+}
+
+// regular timeout attribute will affect query timeout, causing this query to fail
+$db = new PDO($dsn, $user, $pass, [PDO::ATTR_TIMEOUT => 1]);
+$stmt = $db->prepare($sql);
+if (!$stmt->execute()) {
+ echo "OK\n";
+
+ // expect some kind of error code
+ if ($stmt->errorCode() != '00000') {
+ echo "OK\n";
+ }
+}
+
+// pdo_dblib-specific timeout attribute will control query timeout, causing this query to fail
+$db = new PDO($dsn, $user, $pass, [PDO::DBLIB_ATTR_QUERY_TIMEOUT => 1]);
+$stmt = $db->prepare($sql);
+if (!$stmt->execute()) {
+ echo "OK\n";
+
+ // expect some kind of error code
+ if ($stmt->errorCode() != '00000') {
+ echo "OK\n";
+ }
+}
+
+?>
+--EXPECT--
+OK
+OK
+OK
+OK
+OK
diff --git a/ext/pdo_dblib/tests/types.phpt b/ext/pdo_dblib/tests/types.phpt
new file mode 100644
index 0000000000..dd849adcf8
--- /dev/null
+++ b/ext/pdo_dblib/tests/types.phpt
@@ -0,0 +1,66 @@
+--TEST--
+PDO_DBLIB: Column data types, with or without stringifying
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo_dblib')) die('skip not loaded');
+require __DIR__ . '/config.inc';
+?>
+--FILE--
+<?php
+require __DIR__ . '/config.inc';
+
+$sql = "
+ SELECT
+ 'foo' AS [char],
+ CAST('2030-01-01 23:59:59' AS DATETIME) AS [datetime],
+ CAST(0 AS BIT) AS [false],
+ 10.5 AS [float],
+ 1000 AS [int],
+ CAST(10.5 AS MONEY) AS [money],
+ CAST('1950-01-18 23:00:00' AS SMALLDATETIME) as [smalldatetime],
+ CAST(1 AS BIT) AS [true]
+";
+
+$stmt = $db->query($sql);
+$row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+var_dump($row['char'] === 'foo');
+var_dump($row['datetime'] === '2030-01-01 23:59:59');
+var_dump($row['false'] === 0);
+var_dump($row['float'] === 10.5);
+var_dump($row['int'] === 1000);
+var_dump($row['money'] === 10.5);
+var_dump($row['smalldatetime'] === '1950-01-18 23:00:00');
+var_dump($row['true'] === 1);
+
+$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
+$stmt = $db->query($sql);
+$row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+var_dump($row['char'] === 'foo');
+var_dump($row['datetime'] === '2030-01-01 23:59:59');
+var_dump($row['false'] === '0');
+var_dump($row['float'] === '10.5');
+var_dump($row['int'] === '1000');
+var_dump($row['money'] === '10.5');
+var_dump($row['smalldatetime'] === '1950-01-18 23:00:00');
+var_dump($row['true'] === '1');
+
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)