php-sqlsrv/test/functional/sqlsrv/MsHelper.inc

1245 lines
40 KiB
PHP

<?php
/*
Microsoft SQL Server Driver for PHP - Unit Test Framework
Copyright (c) Microsoft Corporation. All rights reserved.
Description:
Helper classes and functions with Always Encrypted features incorporated.
*/
namespace AE {
require_once('MsSetup.inc');
const KEYSTORE_NONE = 'none';
const KEYSTORE_WIN = 'win';
const KEYSTORE_KSP = 'ksp';
const KEYSTORE_AKV = 'akv';
const INSERT_QUERY = 1;
const INSERT_PREPARE = 2;
const INSERT_QUERY_PARAMS = 3;
const INSERT_PREPARE_PARAMS = 4;
/**
* class for encapsulating column metadata needed for creating a table
*/
class ColumnMeta
{
public $dataType; // a string that includes the size of the type if necessary (e.g., decimal(10,5))
public $colName; // column name
public $encType; // randomized or deterministic; default is deterministic
public $options; // a string that is null by default (e.g. NOT NULL Identity (1,1) )
protected $encryptable; // whether Always Encrypted supports this column
protected $forcedEncrypt; // force column encryption regardless, default to 'false'
public function __construct($dataType, $colName = null, $options = null, $deterministic = true, $noEncrypt = false)
{
if (is_null($colName)) {
$this->colName = getDefaultColname($dataType);
} else {
$this->colName = $colName;
}
$this->forcedEncrypt = false;
$this->encType = ($deterministic ? "deterministic" : "randomized");
if (empty($dataType)) {
echo "Data type can not be null or empty!\n";
}
$this->dataType = $dataType;
$this->options = $options;
if (!$noEncrypt) {
$this->checkIfUnsupported();
} else {
$this->encryptable = false;
}
}
/**
* if Always Encrypted not supported for this data type set $noEncryption to false
* @return void
*/
protected function checkIfUnsupported()
{
// Always Encrypted is not supported for columns with the IDENTITY property
// or any column using one of the following datatypes:
//
// xml, timestamp, image, ntext, text, sql_variant, hierarchyid, geography, geometry, alias,
// user defined-types.
// https://docs.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-database-engine
$unsupported = array("xml", "timestamp", "image", "ntext", "text", "sql_variant", "hierarchyid", "geography", "geometry", "alias");
if (!is_null($this->options) && stripos($this->options, "identity") !== false) {
$this->encryptable = false;
} elseif (in_array(strtolower($this->dataType), $unsupported)) {
$this->encryptable = false;
} else {
$this->encryptable = true;
}
}
/**
* force column to be encrypted regardless of the current settings
* @return void
*/
public function forceEncryption($forceEncryption)
{
$this->forcedEncrypt = $forceEncryption;
}
/**
* @return string column definition for creating a table
*/
public function getColDef()
{
$append = " ";
if (($this->encryptable && isDataEncrypted()) || $this->forcedEncrypt) {
$cekName = getCekName();
if ($this->forcedEncrypt && empty($cekName)) {
$cekName = 'AEColumnKey'; // Use Windows AE key by default
}
if (stripos($this->dataType, "char") !== false) {
$append .= "COLLATE Latin1_General_BIN2 ";
}
$append .= sprintf("ENCRYPTED WITH (ENCRYPTION_TYPE = %s, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = $cekName) ", $this->encType);
}
$append .= $this->options;
$colDef = "[" . $this->colName . "] " . $this->dataType . $append;
return $colDef;
}
}
/**
* class for encapsulating optional parameters for binding parameters in sqlsrv_prepare
*/
class BindParamOption
{
public $value; // the param value
public $direction; // SQLSRV_PARAM_ constant indicating the parameter direction
public $phpType; // SQLSRV_PHPTYPE_ constant specifying the php type of the return values
public $sqlType; // SQLSRV_SQLTYPE_ constant specifying the SQL type of the input
public function __construct($value, $direction = null, $phpType = null, $sqlType = null)
{
$this->value = $value;
$this->direction = $direction;
$this->phpType = $phpType;
$this->sqlType = $sqlType;
}
/**
* @return array needed to bind parameter in sqlsrv_prepare
*/
public function bindParamArr()
{
// get the constant values of direction, phpType, and sqlType
$direction = null;
$phpType = null;
$sqlType = null;
if ($this->direction) {
if (in_array($this->direction, array(SQLSRV_PARAM_IN, SQLSRV_PARAM_OUT, SQLSRV_PARAM_INOUT))) {
$direction = constant($this->direction);
} else {
echo "BindParamOption: invalid direction for parameter!\n";
}
}
if ($this->phpType) {
try {
$arr = explode("(", $this->phpType);
$type = $arr[0];
if (count($arr) > 1) {
$enc = rtrim($arr[1], ")");
$phpType = call_user_func($type, constant($enc));
} else {
$phpType = constant($this->phpType);
}
} catch (Exception $e) {
// there's something wrong with the input php type
echo $e->getMessage();
}
}
if ($this->sqlType) {
// parse the datatype name, size, precision, and/or scale from a SQLSRV_SQLTYPE_ constant
$size = null;
$prec = null;
$scal = null;
$type_size = explode("(", $this->sqlType);
$type = $type_size[0];
if (count($type_size) > 1) {
$size = rtrim($type_size[1], ")");
$prec_scal = explode(",", $size);
if (count($prec_scal) > 1) {
$prec = $prec_scal[0];
$scal = $prec_scal[1];
$size = null;
}
if (!is_null($size) && strpos($size, "max") !== false) {
$size = trim($size, "'");
}
}
// get the sqlType constant
try {
if ($prec && $scal) {
$sqlType = call_user_func($type, $prec, $scal);
} elseif ($size) {
$sqlType = call_user_func($type, $size);
} else {
$sqlType = constant($type);
}
} catch (Exception $e) {
// there's something wrong with the input SQL type
echo $e->getMessage();
}
}
return array($this->value, $direction, $phpType, $sqlType);
}
}
/**
* @return string CEK name depending on the connection keywords
*/
function getCekName()
{
global $keystore;
$cekName = '';
switch ($keystore) {
case KEYSTORE_NONE:
$cekName = '';
break;
case KEYSTORE_WIN:
$cekName = 'AEColumnKey';
break;
case KEYSTORE_KSP:
$cekName = 'CustomCEK';
break;
case KEYSTORE_AKV:
$cekName = 'AKVColumnKey';
break;
default:
echo "getCekName: Invalid keystore name.\n";
}
return $cekName;
}
/**
* @return string default column name when a name is not provided in the ColumnMeta class
*/
function getDefaultColname($dataType)
{
$colName = "c_" . str_replace(",", "_", str_replace("(", "_", $dataType));
$colName = rtrim($colName, ")");
return $colName;
}
/**
* @param string $tbname : name of the table for an insert sql
* @param array $input : associative array containing a key value pair of column name and data to put into an insert sql string
* @return string a complete insert sql string
*/
function getInsertSqlComplete($tbname, $inputs)
{
$colStr = "INSERT INTO [$tbname] (";
$valStr = "VALUES (";
if (empty($inputs)) {
echo "getInsertSqlComplete: inputs for inserting a row cannot be empty.\n";
return;
}
foreach ($inputs as $key => $value) {
$colStr .= $key . ", ";
if (is_null($value)) {
$valStr .= "null, ";
} elseif (is_string($value)) {
$valStr .= "'" . $value . "', ";
} else {
$valStr .= $value . ", ";
}
}
$colStr = rtrim($colStr, ", ") . ") ";
$valStr = rtrim($valStr, ", ") . ") ";
$insertSql = $colStr . $valStr;
return $insertSql;
}
/**
* @param string $tbname : name of the table for an insert sql
* @param array $inputs : associative array containing a key value pair of column name and data to put into an insert sql string
* @return string an insert sql string with "?" placeholders for all values
*/
function getInsertSqlPlaceholders($tbname, $inputs)
{
$colStr = "INSERT INTO [$tbname] (";
$valStr = "VALUES (";
if (empty($inputs)) {
echo "getInsertSqlPlaceholders: inputs for inserting a row cannot be empty.\n";
return;
}
foreach ($inputs as $key => $value) {
$colStr .= $key . ", ";
}
$colStr = rtrim($colStr, ", ") . ") ";
$valStr .= getSeqPlaceholders(count($inputs)) . ") ";
$insertSql = $colStr . $valStr;
return $insertSql;
}
/**
* @param string $spname : name of the stored procedure
* @param int $num : number of parameters needed for the stored procedure
* @return string a call stored procedure sql string with "?" placeholders for all parameters
*/
function getCallProcSqlPlaceholders($spname, $num)
{
$callStr = "{CALL [$spname] (";
$callStr .= getSeqPlaceholders($num) . ")} ";
return $callStr;
}
/**
* @param int $num : number of placeholders needed
* @return string a string containing $num number of repeated "?" placeholders delimited by ", "
*/
function getSeqPlaceholders($num)
{
if ($num <= 0) {
echo "getSeqPlaceholders: num provided for creating a sequence of placeholders cannot be less than 0.\n";
return;
}
$placeholderStr = str_repeat("?, ", $num);
$placeholderStr = rtrim($placeholderStr, ", ");
return $placeholderStr;
}
/**
* @return bool false if $keystore specified in MsSetup.inc is none; return true otherwise
*/
function isColEncrypted()
{
global $keystore;
return ($keystore !== KEYSTORE_NONE);
}
/**
* @return bool false if $keystore specified in MsSetup.inc is none or data not encrypted;
* return true otherwise
*/
function isDataEncrypted()
{
global $keystore, $dataEncrypted;
return ($keystore !== KEYSTORE_NONE && $dataEncrypted);
}
/**
* @return bool false if ODBC version is less than 17 or SQL Server older than 2016
*/
function isQualified($conn)
{
$msodbcsql_ver = sqlsrv_client_info($conn)['DriverVer'];
if (explode(".", $msodbcsql_ver)[0] < 17) {
return false;
}
global $daasMode;
if ($daasMode) {
// running against Azure
return true;
}
// if not Azure, check the server version
$server_ver = sqlsrv_server_info($conn)['SQLServerVersion'];
if (explode('.', $server_ver)[0] < 13) {
return false;
}
return true;
}
/**
* Connect to the database specified in MsSetup.inc; Column Encryption options automatically added when $keystore is not none
* @param array $options : connection attributes to pass to sqlsrv_connect
* @param bool $disableCE : flag for disabling column encryption even when keystore is NOT none
* for testing fetching encrypted data when connection column encryption is off
* @return connection resource
*/
function connect($options = array(), $disableCE = false)
{
require('MsSetup.inc');
if (!empty($options)) {
$connectionOptions = array_merge($connectionOptions, $options);
}
if (!$disableCE) {
if (isColEncrypted()) {
$connectionOptions = array_merge($connectionOptions, array("ColumnEncryption" => "Enabled"));
}
if ($keystore == 'akv') {
$akv_options = array("KeyStoreAuthentication"=>$AKVKeyStoreAuthentication);
if ($AKVKeyStoreAuthentication == "KeyVaultPassword") {
$akv_options["KeyStorePrincipalId"] = $AKVPrincipalName;
$akv_options["KeyStoreSecret"] = $AKVPassword;
} else if ($AKVKeyStoreAuthentication == "KeyVaultClientSecret") {
$akv_options["KeyStorePrincipalId"] = $AKVClientID;
$akv_options["KeyStoreSecret"] = $AKVSecret;
}
$connectionOptions = array_merge($connectionOptions, $akv_options);
}
}
$conn = sqlsrv_connect($server, $connectionOptions);
if ($conn === false) {
fatalError("Failed to connect to $server.");
}
return $conn;
}
/**
* Create a table
* @param resource $conn : sqlsrv connection resource
* @param string $tbname : name of the table to be created
* @param array $columnMetaArr : array of ColumnMeta objects, which contain metadata for one column
* @return resource sqlsrv statement resource
*/
function createTable($conn, $tbname, $columnMetaArr)
{
require_once("MsCommon.inc");
dropTable($conn, $tbname);
$colDef = "";
foreach ($columnMetaArr as $meta) {
$colDef = $colDef . $meta->getColDef() . ", ";
}
$colDef = rtrim($colDef, ", ");
$createSql = "CREATE TABLE [$tbname] ( $colDef )";
return sqlsrv_query($conn, $createSql);
}
/**
* Insert a row into a table
* @param resource $conn : sqlsrv connection resource
* @param string $tbname : name of the table for the row to be inserted
* @param array $inputs : an associative array column name and its value, which may be a
* literal or a BindParamOption object
* @param bool $r : true if the row was successfully inserted, otherwise false. Default value is null to make this parameter optional.
* $param int $api : SQLSRV API used for executing the insert query
* accepted values: INSERT_QUERY, INSERT_PREPARE, INSERT_QUERY_PARAMS, INSERT_PREPARE_PARAMS
* @return resource sqlsrv statement resource of the insert statement
*/
function insertRow($conn, $tbname, $inputs, &$r = null, $api = INSERT_QUERY)
{
$stmt = null;
if (!isColEncrypted() && $api < INSERT_QUERY_PARAMS) {
$insertSql = getInsertSqlComplete($tbname, $inputs);
switch ($api) {
case INSERT_QUERY:
$stmt = sqlsrv_query($conn, $insertSql);
break;
case INSERT_PREPARE:
$stmt = sqlsrv_prepare($conn, $insertSql);
if ($stmt) {
$r = sqlsrv_execute($stmt);
}
break;
}
} else {
// must bind param
$insertSql = getInsertSqlPlaceholders($tbname, $inputs);
$params = array();
foreach ($inputs as $key => $input) {
if (is_object($input)) {
array_push($params, $input->bindParamArr());
} else {
array_push($params, $inputs[$key]);
}
}
// use prepare for inserts when AE is enabled
if (isColEncrypted() || $api == INSERT_PREPARE_PARAMS) {
$stmt = sqlsrv_prepare($conn, $insertSql, $params);
if ($stmt) {
$r = sqlsrv_execute($stmt);
} else {
fatalError("insertRow: failed to prepare insert query!");
}
} else {
$stmt = sqlsrv_query($conn, $insertSql, $params);
}
}
return $stmt;
}
/**
* Perform a simple select from a table with or without where condition(s)
* @param resource $conn : connection resource
* @param string $tbname : name of the table
* @param string $conds : string of condition(s) with placeholders, null by default
* @param array $values : array of parameters, null by default
* @return resource sqlsrv statement upon success or false otherwise
*/
function selectFromTable($conn, $tbname, $conds = null, $values = null)
{
$sql = "SELECT * FROM $tbname";
return executeQuery($conn, $sql, $conds, $values);
}
/**
* Perform a query from a table with or without where condition(s)
* @param resource $conn : connection resource
* @param string $sql : T-SQL query
* @param string $conds : string of condition(s) possibly with placeholders, null by default
* @param array $values : array of parameters for placeholders in $conds, null by default
* @param array $options : array of query options, null by default
* @return resource sqlsrv statement upon success or false otherwise
*/
function executeQuery($conn, $sql, $conds = null, $values = null, $options = null)
{
if (!isColEncrypted()) {
// if not encrypted replace placeholders ('?') with values, if array not empty
if (!empty($values)) {
$tmpArray = explode('?', $conds);
$i = 0;
foreach ($values as $value) {
if (!is_numeric($value)) {
$tmpArray[$i++] .= "'$value'";
} else {
$tmpArray[$i++] .= "$value";
}
}
$clause = implode($tmpArray);
$sql = $sql . " WHERE $clause ";
} elseif (!empty($conds)) {
$sql = $sql . " WHERE $conds ";
}
$stmt = sqlsrv_query($conn, $sql, null, $options);
} else {
// with AE enabled, use sqlsrv_prepare() in case there are
// fields with unlimited size
if (empty($conds)) {
$stmt = sqlsrv_prepare($conn, $sql, null, $options);
} else {
$sql = $sql . " WHERE $conds ";
// pass $values to sqlsrv_prepare whether the array is null, empty or filled
$stmt = sqlsrv_prepare($conn, $sql, $values, $options);
}
if ($stmt) {
$r = sqlsrv_execute($stmt);
if (!$r) {
fatalError("executeQuery: failed to execute \'$sql\'!");
}
}
}
if (!$stmt) {
fatalError("executeQuery: failed to run query \'$sql\'!");
}
return $stmt;
}
/**
* Similar to executeQuery() but with query options
* @return resource sqlsrv statement upon success or false otherwise
*/
function executeQueryEx($conn, $sql, $options)
{
// when AE is enabled, use sqlsrv_prepare() to handle fields with unlimited size
if (!isColEncrypted()) {
$stmt = sqlsrv_query($conn, $sql, null, $options);
} else {
$stmt = sqlsrv_prepare($conn, $sql, null, $options);
if ($stmt) {
$r = sqlsrv_execute($stmt);
if (!$r) {
fatalError("executeQueryEx: failed to execute \'$sql\'!");
}
}
}
if (!$stmt) {
fatalError("executeQueryEx: failed to run query \'$sql\'!");
}
return $stmt;
}
/**
* Similar to executeQuery() but with params for binding and
* whether this query is expected to fail
* @return resource sqlsrv statement upon success or false otherwise
*/
function executeQueryParams($conn, $sql, $params, $expectedToFail = false, $message = '')
{
$res = true;
if (isColEncrypted()) {
$stmt = sqlsrv_prepare($conn, $sql, $params);
if ($stmt) {
$res = sqlsrv_execute($stmt);
}
} else {
$stmt = sqlsrv_query($conn, $sql, $params);
}
if ($stmt === false || !$res ) {
if (! $expectedToFail) {
fatalError($message);
} else {
print_r(sqlsrv_errors());
}
} else {
if ($expectedToFail) {
fatalError($message);
}
}
return $stmt;
}
/**
* Fetch all rows and all columns given a table name, and print them
* @param resource $conn : connection resource
* @param string $tbname : name of the table to fetch from
*/
function fetchAll($conn, $tbname)
{
$stmt = selectFromTable($conn, $tbname);
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
foreach ($row as $key => $value) {
if (is_object($value)) {
print "$key:\n";
foreach ($value as $k =>$v) {
print(" $k: $v\n");
}
} else {
print("$key: $value\n");
}
}
}
}
/**
* Create a test table with columns of various types given a table name,
* all deterministic if AE is enabled
* @param resource $conn : connection resource
* @param string $tbname : name of the table to create
* @return resource sqlsrv statement
*/
function createTestTable($conn, $tbname)
{
$columns = array(new ColumnMeta('int', 'c1_int'),
new ColumnMeta('tinyint', 'c2_tinyint'),
new ColumnMeta('smallint', 'c3_smallint'),
new ColumnMeta('bigint', 'c4_bigint'),
new ColumnMeta('bit', 'c5_bit'),
new ColumnMeta('float', 'c6_float'),
new ColumnMeta('real', 'c7_real'),
new ColumnMeta('decimal(28,4)', 'c8_decimal'),
new ColumnMeta('numeric(32,4)', 'c9_numeric'),
new ColumnMeta('money', 'c10_money', null, true, true),
new ColumnMeta('smallmoney', 'c11_smallmoney', null, true, true),
new ColumnMeta('char(512)', 'c12_char'),
new ColumnMeta('varchar(512)', 'c13_varchar'),
new ColumnMeta('varchar(max)', 'c14_varchar_max'),
new ColumnMeta('nchar(512)', 'c15_nchar'),
new ColumnMeta('nvarchar(512)', 'c16_nvarchar'),
new ColumnMeta('nvarchar(max)', 'c17_nvarchar_max'),
new ColumnMeta('text', 'c18_text'),
new ColumnMeta('ntext', 'c19_ntext'),
new ColumnMeta('binary(512)', 'c20_binary'),
new ColumnMeta('varbinary(512)', 'c21_varbinary'),
new ColumnMeta('varbinary(max)', 'c22_varbinary_max'),
new ColumnMeta('image', 'c23_image'),
new ColumnMeta('uniqueidentifier', 'c24_uniqueidentifier'),
new ColumnMeta('datetime', 'c25_datetime'),
new ColumnMeta('smalldatetime', 'c26_smalldatetime'),
new ColumnMeta('timestamp', 'c27_timestamp'),
new ColumnMeta('xml', 'c28_xml'),
);
return createTable($conn, $tbname, $columns);
}
/**
* Insert the specified number of rows with test data into the test table
* @param resource $conn : connection resource
* @param string $tbname : name of the table to create
* @param int $rowCount : number of test rows to be inserted
* @return number of rows inserted
*/
function insertTestRows($conn, $tbame, $rowCount)
{
$count = 0;
for ($i = 0; $i < $rowCount; $i++) {
if (insertTestRow($conn, $tbame, rand(1, 20))) {
$count++;
}
}
if ($count != $rowCount) {
die("$count rows inserted instead of $rowCount\n");
}
return ($count);
}
/**
* Insert a number of rows with test data into the test table given the indexes
* @param resource $conn : connection resource
* @param string $tbname : name of the table to create
* @param int $minIndex : index of the first row to be inserted
* @param int $maxIndex : index of the last row to be inserted
* @return number of rows inserted
*/
function insertTestRowsByRange($conn, $tbame, $minIndex, $maxIndex)
{
$count = 0;
$rowCount = $maxIndex - $minIndex + 1;
if ($rowCount > 0) {
for ($i = $minIndex; $i <= $maxIndex; $i++) {
if (insertTestRow($conn, $tbame, $i)) {
$count++;
}
}
}
if ($count != $rowCount) {
die("$count rows inserted instead of $rowCount\n");
}
return ($count);
}
/**
* Insert one or more specific rows of test data into the test table
* @param resource $conn : connection resource
* @param string $tbname : name of the table to create
* @param int $index : the index of a certain row of test data
* @return resource sqlsrv statement upon success or false otherwise
*/
function insertTestRow($conn, $tbname, $index)
{
if ($index < 1 || $index > 20) {
echo("Invalid row index $index for test data!\n");
return false;
}
// get array of input values
$inputArray = getInsertArray($index);
if (empty($inputArray)) {
fatalError("getInsertSqlComplete: inputs for inserting a row cannot be empty");
}
$result = null;
if (isColEncrypted()) {
$stmt = insertRow($conn, $tbname, $inputArray, $result);
} else {
// do not use the generic insertRow as the binary data needs some pre-processing
// before calling sqlsrv_query()
$colStr = "INSERT INTO $tbname (";
$valStr = "VALUES (";
$col = 1;
foreach ($inputArray as $key => $value) {
$colStr .= $key . ", ";
if (is_array($value)) {
$value = $value[0];
// this might be an input to a decimal, a numeric or a binary field
if (isBinary($col)) {
if (!is_null($value)) {
$value = "0x" . $value; // annotate the input string as a hex string
} else {
$value = null;
}
}
}
if (is_null($value)) {
$valStr .= "null, ";
} elseif (is_string($value) && !isBinary($col)) {
$valStr .= "'" . $value . "', ";
} else {
$valStr .= $value . ", ";
}
$col++;
}
$colStr = rtrim($colStr, ", ") . ") ";
$valStr = rtrim($valStr, ", ") . ") ";
$insertSql = $colStr . $valStr;
$stmt = sqlsrv_query($conn, $insertSql);
}
if (!$stmt || $result === false) {
fatalError("insertTestRow: failed to insert row $index!\n");
}
return $stmt;
}
/**
* Get a row of data to be inserted into the test table
* @param int $index : the index of a certain row of test data
* @return an array of key-value pairs
*/
function getInsertArray($index)
{
if (! UseUTF8data()) {
require_once('MsData.inc');
return getTestData($index);
} else {
require_once('MsData_UTF8.inc');
return getTestData_UTF8($index);
}
// get array of input values
}
/**
* Get test data based on the row and column
* @param int $rowIndex : the row index
* @param int $colIndex : the column index
* @return data of mixed type or null if something went wrong
*/
function getInsertData($rowIndex, $colIndex)
{
// get array of input values
$inputArray = getInsertArray($rowIndex);
if (empty($inputArray)) {
fatalError("getInsertData: failed to retrieve data at row $rowIndex.\n");
}
$key = getColName($colIndex);
if (!empty($key)) {
$value = $inputArray[$key];
if (is_array($value)) {
return $value[0];
} else {
return $value;
}
}
return null;
}
} // end of namespace AlwaysEncrypted
namespace {
function getSqlType($k)
{
switch ($k) {
case 1: return ("int");
case 2: return ("tinyint");
case 3: return ("smallint");
case 4: return ("bigint");
case 5: return ("bit");
case 6: return ("float");
case 7: return ("real");
case 8: return ("decimal(28,4)");
case 9: return ("numeric(32,4)");
case 10: return ("money");
case 11: return ("smallmoney");
case 12: return ("char(512)");
case 13: return ("varchar(512)");
case 14: return ("varchar(max)");
case 15: return ("nchar(512)");
case 16: return ("nvarchar(512)");
case 17: return ("nvarchar(max)");
case 18: return ("text");
case 19: return ("ntext");
case 20: return ("binary(512)");
case 21: return ("varbinary(512)");
case 22: return ("varbinary(max)");
case 23: return ("image");
case 24: return ("uniqueidentifier");
case 25: return ("datetime");
case 26: return ("smalldatetime");
case 27: return ("timestamp");
case 28: return ("xml");
default: break;
}
return ("udt");
}
function getSqlsrvSqlType($k, $dataSize)
{
switch ($k) {
case 1: return (SQLSRV_SQLTYPE_INT);
case 2: return (SQLSRV_SQLTYPE_TINYINT);
case 3: return (SQLSRV_SQLTYPE_SMALLINT);
case 4: return (SQLSRV_SQLTYPE_BIGINT);
case 5: return (SQLSRV_SQLTYPE_BIT);
case 6: return (SQLSRV_SQLTYPE_FLOAT);
case 7: return (SQLSRV_SQLTYPE_REAL);
case 8: return (SQLSRV_SQLTYPE_DECIMAL(28, 4));
case 9: return (SQLSRV_SQLTYPE_NUMERIC(32, 4));
case 10: return (SQLSRV_SQLTYPE_MONEY);
case 11: return (SQLSRV_SQLTYPE_SMALLMONEY);
case 12: return (SQLSRV_SQLTYPE_CHAR($dataSize));
case 13: return (SQLSRV_SQLTYPE_VARCHAR($dataSize));
case 14: return (SQLSRV_SQLTYPE_VARCHAR('max'));
case 15: return (SQLSRV_SQLTYPE_NCHAR($dataSize));
case 16: return (SQLSRV_SQLTYPE_NVARCHAR($dataSize));
case 17: return (SQLSRV_SQLTYPE_NVARCHAR('max'));
case 18: return (SQLSRV_SQLTYPE_TEXT);
case 19: return (SQLSRV_SQLTYPE_NTEXT);
case 20: return (SQLSRV_SQLTYPE_BINARY($dataSize));
case 21: return (SQLSRV_SQLTYPE_VARBINARY($dataSize));
case 22: return (SQLSRV_SQLTYPE_VARBINARY('max'));
case 23: return (SQLSRV_SQLTYPE_IMAGE);
case 24: return (SQLSRV_SQLTYPE_UNIQUEIDENTIFIER);
case 25: return (SQLSRV_SQLTYPE_DATETIME);
case 26: return (SQLSRV_SQLTYPE_SMALLDATETIME);
case 27: return (SQLSRV_SQLTYPE_TIMESTAMP);
case 28: return (SQLSRV_SQLTYPE_XML);
default: break;
}
return (SQLSRV_SQLTYPE_UDT);
}
function isXml($k)
{
switch ($k) {
case 28: return (true); // xml
default: break;
}
return (false);
}
function isStreamable($k)
{
switch ($k) {
case 12: return (true); // char(512)
case 13: return (true); // varchar(512)
case 14: return (true); // varchar(max)
case 15: return (true); // nchar(512)
case 16: return (true); // nvarchar(512)
case 17: return (true); // nvarchar(max)
case 18: return (true); // text
case 19: return (true); // ntext
case 20: return (true); // binary
case 21: return (true); // varbinary(512)
case 22: return (true); // varbinary(max)
case 23: return (true); // image
case 28: return (true); // xml
default: break;
}
return (false);
}
function isNumeric($k)
{
switch ($k) {
case 1: return (true); // int
case 2: return (true); // tinyint
case 3: return (true); // smallint
case 4: return (true); // bigint
case 5: return (true); // bit
case 6: return (true); // float
case 7: return (true); // real
case 8: return (true); // decimal(28,4)
case 9: return (true); // numeric(32,4)
case 10: return (true); // money
case 11: return (true); // smallmoney
default: break;
}
return (false);
}
function isChar($k)
{
switch ($k) {
case 12: return (true); // nchar(512)
case 13: return (true); // varchar(512)
case 14: return (true); // varchar(max)
case 15: return (true); // nchar(512)
case 16: return (true); // nvarchar(512)
case 17: return (true); // nvarchar(max)
case 18: return (true); // text
case 19: return (true); // ntext
case 28: return (true); // xml
default: break;
}
return (false);
}
function isBinary($k)
{
switch ($k) {
case 20: return (true); // binary
case 21: return (true); // varbinary(512)
case 22: return (true); // varbinary(max)
case 23: return (true); // image
default: break;
}
return (false);
}
function isDateTime($k)
{
switch ($k) {
case 25: return (true); // datetime
case 26: return (true); // smalldatetime
case 27: return (true); // timestamp
default: break;
}
return (false);
}
function isUnicode($k)
{
switch ($k) {
case 15: return (true); // nchar(512)
case 16: return (true); // nvarchar(512)
case 17: return (true); // nvarchar(max)
case 19: return (true); // ntext
default: break;
}
return (false);
}
function isUpdatable($k)
{
return ($k != 27); // timestamp
}
function isLiteral($k)
{
switch ($k) {
case 12: return (true); // nchar(512)
case 13: return (true); // varchar(512)
case 14: return (true); // varchar(max)
case 15: return (true); // nchar(512)
case 16: return (true); // nvarchar(512)
case 17: return (true); // nvarchar(max)
case 18: return (true); // text
case 19: return (true); // ntext
case 24: return (true); // uniqueidentifier
case 25: return (true); // datetime
case 26: return (true); // smalldatetime
case 28: return (true); // xml
default: break;
}
return (false);
}
function getMetadata($k, $info)
{
if (strcasecmp($info, 'Name') == 0) {
return (getColName($k));
}
if (strcasecmp($info, 'Size') == 0) {
return (getColSize($k));
}
if (strcasecmp($info, 'Precision') == 0) {
return (getColPrecision($k));
}
if (strcasecmp($info, 'Scale') == 0) {
return (getColScale($k));
}
if (strcasecmp($info, 'Nullable') == 0) {
return (getColNullable($k));
}
return ("");
}
function getColName($k)
{
switch ($k) {
case 1: return ("c1_int");
case 2: return ("c2_tinyint");
case 3: return ("c3_smallint");
case 4: return ("c4_bigint");
case 5: return ("c5_bit");
case 6: return ("c6_float");
case 7: return ("c7_real");
case 8: return ("c8_decimal");
case 9: return ("c9_numeric");
case 10: return ("c10_money");
case 11: return ("c11_smallmoney");
case 12: return ("c12_char");
case 13: return ("c13_varchar");
case 14: return ("c14_varchar_max");
case 15: return ("c15_nchar");
case 16: return ("c16_nvarchar");
case 17: return ("c17_nvarchar_max");
case 18: return ("c18_text");
case 19: return ("c19_ntext");
case 20: return ("c20_binary");
case 21: return ("c21_varbinary");
case 22: return ("c22_varbinary_max");
case 23: return ("c23_image");
case 24: return ("c24_uniqueidentifier");
case 25: return ("c25_datetime");
case 26: return ("c26_smalldatetime");
case 27: return ("c27_timestamp");
case 28: return ("c28_xml");
default:
fatalError("Invalid column index $k");
break;
}
return ("");
}
function isStreamData($k)
{
switch ($k) {
case 14: return (true); // varchar(max)
case 17: return (true); // nvarchar(max)
case 18: return (true); // text
case 19: return (true); // ntext
case 20: return (true); // binary
case 21: return (true); // varbinary(512)
case 22: return (true); // varbinary(max)
case 23: return (true); // image
case 27: return (true); // timestamp
case 28: return (true); // xml
default: break;
}
return (false);
}
function getColSize($k)
{
switch ($k) {
case 12: return ("512");
case 13: return ("512");
case 14: return ("0");
case 15: return ("512");
case 16: return ("512");
case 17: return ("0");
case 18: return ("2147483647");
case 19: return ("1073741823");
case 20: return ("512");
case 21: return ("512");
case 22: return ("0");
case 23: return ("2147483647");
case 24: return ("36");
//case 25: return ("23");
//case 26: return ("16");
case 27: return ("8");
case 28: return ("0");
default: break;
}
return NULL;
}
function getColPrecision($k)
{
switch ($k) {
case 1: return ("10");
case 2: return ("3");
case 3: return ("5");
case 4: return ("19");
case 5: return ("1");
case 6: return ("53");
case 7: return ("24");
case 8: return ("28");
case 9: return ("32");
case 10: return ("19");
case 11: return ("10");
case 25: return ("23");
case 26: return ("16");
default: break;
}
return ("");
}
function getColScale($k)
{
switch ($k) {
case 8: return ("4");
case 9: return ("4");
case 10: return ("4");
case 11: return ("4");
case 25: return ("3");
case 26: return ("0");
default: break;
}
return ("");
}
function getColNullable($k)
{
return (isUpdatable($k) ? "1" : "0");
}
function getSampleData($k)
{
switch ($k) {
case 1: // int
return ("123456789");
case 2: // tinyint
return ("234");
case 3: // smallint
return ("5678");
case 4: // bigint
return ("123456789987654321");
case 5: // bit
return ("1");
case 6: // float
return ("123.456");
case 7: // real
return ("789.012");
case 8: // decimal
return ("12.34");
case 9: // numeric
return ("567.89");
case 10:// money
return ("321.54");
case 11:// smallmoney
return ("67.89");
case 12:// char
case 15:// nchar
return ("The quick brown fox jumps over the lazy dog");
case 13:// varchar
case 16:// nvarchar
return ("The quick brown fox jumps over the lazy dog 9876543210");
case 14:// varchar(max)
case 17:// nvarchar(max)
return ("The quick brown fox jumps over the lazy dog 0123456789");
case 18:// text
case 19:// ntext
return ("0123456789 The quick brown fox jumps over the lazy dog");
case 20:// binary
return ("0123456789");
case 21:// varbinary
return ("01234567899876543210");
case 22:// varbinary(max)
return ("98765432100123456789");
case 23:// image
return ("01234567899876543210");
case 24:// uniqueidentifier
return ("12345678-9012-3456-7890-123456789012");
case 25:// datetime
case 26:// smalldatetime
return (date("Y-m-d"));
case 27:// timestamp
return (null);
case 28:// xml
return ("<XmlTestData><Letters1>The quick brown fox jumps over the lazy dog</Letters1><Digits1>0123456789</Digits1></XmlTestData>");
default:
break;
}
return (null);
}
}
?>