diff --git a/ext/pdo_oci/oci_statement.c b/ext/pdo_oci/oci_statement.c
index 44efa0de0a..79733c2c57 100644
--- a/ext/pdo_oci/oci_statement.c
+++ b/ext/pdo_oci/oci_statement.c
@@ -2,7 +2,7 @@
   +----------------------------------------------------------------------+
   | PHP Version 7                                                        |
   +----------------------------------------------------------------------+
-  | Copyright (c) 1997-2018 The PHP Group                                |
+  | Copyright (c) The PHP Group                                          |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
@@ -527,7 +525,7 @@ static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
 	OCIParam *param = NULL;
 	text *colname;
-	ub2 dtype, data_size, scale, precis;
+	ub2 dtype, data_size, precis;
 	ub4 namelen;
 	struct pdo_column_data *col = &stmt->columns[colno];
 	zend_bool dyn = FALSE;
@@ -543,10 +541,6 @@ static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE",
 			(param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err));
 
-	/* scale ? */
-	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE",
-			(param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));
-
 	/* precision ? */
 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
 			(param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
@@ -555,7 +549,7 @@ static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME",
 			(param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err));
 
-	col->precision = scale;
+	col->precision = precis;
 	col->maxlen = data_size;
 	col->name = zend_string_init((char *)colname, namelen, 0);
 
@@ -600,7 +594,7 @@ static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
 				S->cols[colno].datalen = 1024;
 #endif
 			} else if (dtype == SQLT_BIN) {
-				S->cols[colno].datalen = (ub4) col->maxlen * 2; // raw characters to hex digits
+				S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */
 			} else {
 				S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width);
 			}
@@ -719,7 +713,7 @@ static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend
 	}
 }
 
-static php_stream_ops oci_blob_stream_ops = {
+static const php_stream_ops oci_blob_stream_ops = {
 	oci_blob_write,
 	oci_blob_read,
 	oci_blob_close,
@@ -795,20 +789,207 @@ static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len
 	}
 } /* }}} */
 
-struct pdo_stmt_methods oci_stmt_methods = {
+
+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;
+	OCIParam *param = NULL;
+	ub2 dtype, precis;
+	sb1 scale;
+	zval flags;
+	ub1 isnull, charset_form;
+	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*)&param, 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 precision */
+	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
+			(param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
+
+	/* column scale */
+	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE",
+			(param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));
+
+	/* string column charset form */
+	if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) {
+		STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_CHARSET_FORM",
+			(param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, 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_FLT :
+		case SQLT_NUM:
+			/* if the precision is nonzero and scale is -127 then it is a FLOAT */
+			if (scale == -127 && precis != 0) {
+				add_assoc_string(return_value, "oci:decl_type", "FLOAT");
+				add_assoc_string(return_value, "native_type", "FLOAT");
+			} else {
+				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:
+		case SQLT_VCS:
+			if (charset_form == SQLCS_NCHAR) {
+				add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2");
+				add_assoc_string(return_value, "native_type", "NVARCHAR2");
+			} else {
+				add_assoc_string(return_value, "oci:decl_type", "VARCHAR2");
+				add_assoc_string(return_value, "native_type", "VARCHAR2");
+			}
+			break;
+		case SQLT_AFC:
+			if (charset_form == SQLCS_NCHAR) {
+				add_assoc_string(return_value, "oci:decl_type", "NCHAR");
+				add_assoc_string(return_value, "native_type", "NCHAR");
+			} else {
+				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:
+			if (charset_form == SQLCS_NCHAR) {
+				add_assoc_string(return_value, "oci:decl_type", "NCLOB");
+				add_assoc_string(return_value, "native_type", "NCLOB");
+			} else {
+				add_assoc_string(return_value, "oci:decl_type", "CLOB");
+				add_assoc_string(return_value, "native_type", "CLOB");
+			}
+			add_next_index_string(&flags, "blob");
+			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_BFLOAT:
+		case SQLT_IBFLOAT:
+			add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT");
+			add_assoc_string(return_value, "native_type", "BINARY_FLOAT");
+			break;
+		case SQLT_BDOUBLE:
+		case SQLT_IBDOUBLE:
+			add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE");
+			add_assoc_string(return_value, "native_type", "BINARY_DOUBLE");
+			break;
+		default:
+			add_assoc_long(return_value, "oci:decl_type", dtype);
+			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));
+
+	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;
+		default:
+			add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
+	}
+
+	add_assoc_long(return_value, "scale", scale);
+	add_assoc_zval(return_value, "flags", &flags);
+
+	OCIDescriptorFree(param, OCI_DTYPE_PARAM);
+	return SUCCESS;
+} /* }}} */
+
+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
- * c-basic-offset: 4
- * End:
- * vim600: noet sw=4 ts=4 fdm=marker
- * vim<600: noet sw=4 ts=4
- */