From: Letargie Date: Thu, 20 Sep 2018 10:03:57 +0000 (+0200) Subject: Add the PDOStatement::getColumnMeta() function to the pdo_oci driver X-Git-Tag: php-7.4.0alpha1~1803^2~4^2^2~6 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d25d9276ff4fa40ee987374d9d4e75933f287c33;p=php Add the PDOStatement::getColumnMeta() function to the pdo_oci driver --- diff --git a/ext/pdo_oci/oci_statement.c b/ext/pdo_oci/oci_statement.c index f2c43b9a69..e5d40e872e 100644 --- a/ext/pdo_oci/oci_statement.c +++ b/ext/pdo_oci/oci_statement.c @@ -793,15 +793,198 @@ static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len } } /* }}} */ + +static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + pdo_oci_column *C = &S->cols[colno]; + + OCIParam *param = NULL; + OraText *colname; + OraText * schema; + ub2 dtype, data_size, precis = 0; + ub4 namelen, schemalen, typelen, objlen; + char *str; + zval flags; + ub1 isnull; + if (!S->stmt) { + return FAILURE; + } + if (colno >= stmt->column_count) { + /* error invalid column */ + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)¶m, colno+1)); + + /* column data type */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", + (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* column length */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE", + (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); + /* column precision */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", + (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + + if (dtype) { + // if there is a declared type + switch (dtype) { +#ifdef SQLT_TIMESTAMP + case SQLT_TIMESTAMP: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP"); + add_assoc_string(return_value, "native_type", "TIMESTAMP"); + break; +#endif +#ifdef SQLT_TIMESTAMP_TZ + case SQLT_TIMESTAMP_TZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE"); + break; +#endif +#ifdef SQLT_TIMESTAMP_LTZ + case SQLT_TIMESTAMP_LTZ: + add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE"); + break; +#endif +#ifdef SQLT_INTERVAL_YM + case SQLT_INTERVAL_YM: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH"); + add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH"); + break; +#endif +#ifdef SQLT_INTERVAL_DS + case SQLT_INTERVAL_DS: + add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND"); + add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND"); + break; +#endif + case SQLT_DAT: + add_assoc_string(return_value, "oci:decl_type", "DATE"); + add_assoc_string(return_value, "native_type", "DATE"); + break; + case SQLT_NUM: + add_assoc_string(return_value, "oci:decl_type", "NUMBER"); + add_assoc_string(return_value, "native_type", "NUMBER"); + break; + case SQLT_LNG: + add_assoc_string(return_value, "oci:decl_type", "LONG"); + add_assoc_string(return_value, "native_type", "LONG"); + break; + case SQLT_BIN: + add_assoc_string(return_value, "oci:decl_type", "RAW"); + add_assoc_string(return_value, "native_type", "RAW"); + break; + case SQLT_LBI: + add_assoc_string(return_value, "oci:decl_type", "LONG RAW"); + add_assoc_string(return_value, "native_type", "LONG RAW"); + break; + case SQLT_CHR: + add_assoc_string(return_value, "oci:decl_type", "VARCHAR2"); + add_assoc_string(return_value, "native_type", "VARCHAR2"); + break; + case SQLT_AFC: + add_assoc_string(return_value, "oci:decl_type", "CHAR"); + add_assoc_string(return_value, "native_type", "CHAR"); + break; + case SQLT_BLOB: + add_assoc_string(return_value, "oci:decl_type", "BLOB"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BLOB"); + break; + case SQLT_CLOB: + add_assoc_string(return_value, "oci:decl_type", "CLOB"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "CLOB"); + break; + case SQLT_BFILE: + add_assoc_string(return_value, "oci:decl_type", "BFILE"); + add_next_index_string(&flags, "blob"); + add_assoc_string(return_value, "native_type", "BFILE"); + break; + case SQLT_RDD: + add_assoc_string(return_value, "oci:decl_type", "ROWID"); + add_assoc_string(return_value, "native_type", "ROWID"); + break; + case SQLT_FLT : + case SQLT_BFLOAT: + case SQLT_IBFLOAT: + add_assoc_string(return_value, "oci:decl_type", "FLOAT"); + add_assoc_string(return_value, "native_type", "FLOAT"); + break; + case SQLT_BDOUBLE: + case SQLT_IBDOUBLE: + add_assoc_string(return_value, "oci:decl_type", "DOUBLE"); + add_assoc_string(return_value, "native_type", "DOUBLE"); + break; + default: + add_assoc_long(return_value, "oci:decl_type", dtype); + add_assoc_string(return_value, "native_type", "UNKNOWN"); + } + } else if (data_size) { + // if the column is the result of a function + add_assoc_string(return_value, "native_type", "UNKNOWN"); + } else { + // if the column is NULL + add_assoc_long(return_value, "oci:decl_type", 0); + add_assoc_string(return_value, "native_type", "NULL"); + } + + /* column can be null */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL", + (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err)); + + /* column name */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", + (param, OCI_DTYPE_PARAM, &colname, (ub4 *) &namelen, OCI_ATTR_NAME, S->err)); + + add_assoc_long(return_value, "precision", precis); + add_assoc_long(return_value, "len", data_size); + add_assoc_string(return_value, "name", (char *) colname); + + if (isnull) { + add_next_index_string(&flags, "nullable"); + } else { + add_next_index_string(&flags, "not_null"); + } + + /* PDO type */ + switch (dtype) { + case SQLT_BFILE: + case SQLT_BLOB: + case SQLT_CLOB: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB); + break; + case SQLT_BIN: + default: + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + } + + add_assoc_zval(return_value, "flags", &flags); + OCIDescriptorFree(param, OCI_DTYPE_PARAM); + return SUCCESS; +} /* }}} */ + const struct pdo_stmt_methods oci_stmt_methods = { oci_stmt_dtor, oci_stmt_execute, oci_stmt_fetch, oci_stmt_describe, oci_stmt_get_col, - oci_stmt_param_hook + oci_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + oci_stmt_col_meta }; + /* * Local variables: * tab-width: 4 diff --git a/ext/pdo_oci/tests/pdo_oci_stmt_getcolumnmeta.phpt b/ext/pdo_oci/tests/pdo_oci_stmt_getcolumnmeta.phpt new file mode 100755 index 0000000000..87a1f13543 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_stmt_getcolumnmeta.phpt @@ -0,0 +1,264 @@ +--TEST-- +PDO_OCI: PDOStatement->getColumnMeta +--SKIPIF-- + +--FILE-- +exec(<<exec("CREATE TABLE test(id INT)"); + +$db->beginTransaction(); + +try { + + $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC'); + + // execute() has not been called yet + // NOTE: no warning + if (false !== ($tmp = $stmt->getColumnMeta(0))) + printf("[002] Expecting false got %s\n", var_export($tmp, true)); + + $stmt->execute(); + // Warning: PDOStatement::getColumnMeta() expects exactly 1 parameter, 0 given in + if (false !== ($tmp = @$stmt->getColumnMeta())) + printf("[003] Expecting false got %s\n", var_export($tmp, true)); + + // invalid offset + if (false !== ($tmp = @$stmt->getColumnMeta(-1))) + printf("[004] Expecting false got %s\n", var_export($tmp, true)); + + // Warning: PDOStatement::getColumnMeta() expects parameter 1 to be int, array given in + if (false !== ($tmp = @$stmt->getColumnMeta(array()))) + printf("[005] Expecting false got %s\n", var_export($tmp, true)); + + // Warning: PDOStatement::getColumnMeta() expects exactly 1 parameter, 2 given in + if (false !== ($tmp = @$stmt->getColumnMeta(1, 1))) + printf("[006] Expecting false got %s\n", var_export($tmp, true)); + + $emulated = $stmt->getColumnMeta(0); + + printf("Testing native PS...\n"); + + $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC'); + $stmt->execute(); + $native = $stmt->getColumnMeta(0); + if (count($native) == 0) { + printf("[008] Meta data seems wrong, %s / %s\n", + var_export($native, true), var_export($emulated, true)); + } + + // invalid offset + if (false !== ($tmp = $stmt->getColumnMeta(1))) + printf("[009] Expecting false because of invalid offset got %s\n", var_export($tmp, true)); + + + function test_meta(&$db, $offset, $sql_type, $value, $native_type, $pdo_type) { + + $db->exec(<<prepare($sql)) || (!@$stmt->execute())) { + // Some engines might not support the data type + return true; + } + + if (!$db->exec(sprintf("INSERT INTO test(id, label) VALUES (1, '%s')", $value))) { + printf("[%03d] + 1] Insert failed, %d - %s\n", $offset, + $db->errorCode(), var_export($db->errorInfo(), true)); + return false; + } + + $stmt = $db->prepare('SELECT id, label FROM test'); + $stmt->execute(); + $meta = $stmt->getColumnMeta(1); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if (empty($meta)) { + printf("[%03d + 2] getColumnMeta() failed, %d - %s\n", $offset, + $stmt->errorCode(), var_export($stmt->errorInfo(), true)); + return false; + } + + $elements = array('flags', 'name', 'len', 'precision', 'pdo_type'); + foreach ($elements as $k => $element) + if (!isset($meta[$element])) { + printf("[%03d + 3] Element %s missing, %s\n", $offset, + $element, var_export($meta, true)); + return false; + } + + if (!is_null($native_type)) { + if (!isset($meta['native_type'])) { + printf("[%03d + 5] Element native_type missing, %s\n", $offset, + var_export($meta, true)); + return false; + } + + if (!is_array($native_type)) + $native_type = array($native_type); + + $found = false; + foreach ($native_type as $k => $type) { + if ($meta['native_type'] == $type) { + $found = true; + break; + } + } + + if (!$found) { + printf("[%03d + 6] Expecting native type %s, %s\n", $offset, + var_export($native_type, true), var_export($meta, true)); + return false; + } + } + + if (!is_null($pdo_type) && ($meta['pdo_type'] != $pdo_type)) { + printf("[%03d + 6] Expecting PDO type %s got %s (%s)\n", $offset, + $pdo_type, var_export($meta, true), var_export($meta['native_type'])); + return false; + } + + return true; + } + + test_meta($db, 10, 'NUMBER' , 0 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 20, 'NUMBER' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 20, 'NUMBER' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 20, 'INT' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 20, 'INTEGER' , 256 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 20, 'NUMBER' , 256.01 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 30, 'NUMBER' , -8388608 , 'NUMBER', PDO::PARAM_STR); + + test_meta($db, 40, 'NUMBER' , 2147483648 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 50, 'NUMBER' , 4294967295 , 'NUMBER', PDO::PARAM_STR); + + test_meta($db, 60, 'DECIMAL' , 1.01 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 60, 'FLOAT' , 1.01 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 70, 'DOUBLE' , 1.01 , 'NUMBER', PDO::PARAM_STR); + test_meta($db, 60, 'BINARY_FLOAT' , 1.01 , 'FLOAT', PDO::PARAM_STR); + test_meta($db, 70, 'BINARY_DOUBLE' , 1.01 , 'DOUBLE', PDO::PARAM_STR); + + test_meta($db, 80, 'DATE' , '2008-04-23' , 'DATE', PDO::PARAM_STR); + test_meta($db, 90, 'TIME' , '14:37:00' , 'TIME', PDO::PARAM_STR); + test_meta($db, 110, 'YEAR' , '2008' , 'YEAR', PDO::PARAM_STR); + + test_meta($db, 120, 'CHAR(1)' , 'a' , 'CHAR', PDO::PARAM_STR); + test_meta($db, 130, 'CHAR(10)' , '0123456789' , 'CHAR', PDO::PARAM_STR); + test_meta($db, 140, 'CHAR(255)' , str_repeat('z', 255) , 'CHAR', PDO::PARAM_STR); + test_meta($db, 150, 'VARCHAR(1)' , 'a' , 'VARCHAR2', PDO::PARAM_STR); + test_meta($db, 160, 'VARCHAR(10)' , '0123456789' , 'VARCHAR2', PDO::PARAM_STR); + test_meta($db, 170, 'VARCHAR(255)' , str_repeat('z', 255) , 'VARCHAR2', PDO::PARAM_STR); + test_meta($db, 180, 'VARCHAR2(1)' , 'a' , 'VARCHAR2', PDO::PARAM_STR); + test_meta($db, 190, 'VARCHAR2(10)' , '0123456789' , 'VARCHAR2', PDO::PARAM_STR); + test_meta($db, 200, 'VARCHAR2(255)' , str_repeat('z', 255) , 'VARCHAR2', PDO::PARAM_STR); + + test_meta($db, 210, 'NCHAR(1)' , 'a' , 'CHAR', PDO::PARAM_STR); + test_meta($db, 220, 'NCHAR(10)' , '0123456789' , 'CHAR', PDO::PARAM_STR); + test_meta($db, 230, 'NCHAR(255)' , str_repeat('z', 255) , 'CHAR', PDO::PARAM_STR); + test_meta($db, 240, 'NVARCHAR2(1)' , 'a' , 'VARCHAR2', PDO::PARAM_STR); + test_meta($db, 250, 'NVARCHAR2(10)' , '0123456789' , 'VARCHAR2', PDO::PARAM_STR); + test_meta($db, 260, 'NVARCHAR2(255)', str_repeat('z', 255) , 'VARCHAR2', PDO::PARAM_STR); + + test_meta($db, 270, 'CLOB' , str_repeat('b', 255) , 'CLOB', PDO::PARAM_LOB); + test_meta($db, 280, 'BLOB' , str_repeat('b', 256) , 'BLOB', PDO::PARAM_LOB); + test_meta($db, 290, 'NCLOB' , str_repeat('b', 255) , 'CLOB', PDO::PARAM_LOB); + + test_meta($db, 300, 'LONG' , str_repeat('b', 256) , 'LONG', PDO::PARAM_STR); + test_meta($db, 310, 'LONG RAW' , str_repeat('b', 256) , 'LONG RAW', PDO::PARAM_STR); + test_meta($db, 320, 'RAW' , str_repeat('b', 256) , 'RAW', PDO::PARAM_STR); + + $db->exec(<<prepare($sql)) && $stmt->execute()) { + $db->exec('INSERT INTO test(id, label) VALUES (1, 1)'); + $stmt = $db->query('SELECT id, label FROM test'); + $meta = $stmt->getColumnMeta(0); + if (!isset($meta['flags'])) { + printf("[1002] No flags contained in metadata %s\n", var_export($meta, true)); + } else { + $flags = $meta['flags']; + $found = false; + foreach ($flags as $k => $flag) { + if ($flag == 'not_null') + $found = true; + if ($flag == 'nullable') + printf("[1003] Flags seem wrong %s\n", var_export($meta, true)); + } + if (!$found) + printf("[1003] Flags seem wrong %s\n", var_export($meta, true)); + } + $meta = $stmt->getColumnMeta(1); + if (!isset($meta['flags'])) { + printf("[1002] No flags contained in metadata %s\n", var_export($meta, true)); + } else { + $flags = $meta['flags']; + $found = false; + foreach ($flags as $k => $flag) { + if ($flag == 'not_null') + printf("[1003] Flags seem wrong %s\n", var_export($meta, true)); + if ($flag == 'nullable') + $found = true; + } + if (!$found) + printf("[1003] Flags seem wrong %s\n", var_export($meta, true)); + } + } + +} catch (PDOException $e) { + // we should never get here, we use warnings, but never trust a system... + printf("[001] %s, [%s} %s\n", + $e->getMessage(), $db->errorInfo(), implode(' ', $db->errorInfo())); +} + +$db->exec(<< +--EXPECT-- +Testing native PS... +done!