getAttribute(PDO::ATTR_CLIENT_VERSION)["DriverVer"]; $msodbcsql_maj = explode(".", $msodbcsql_ver)[0]; if ($msodbcsql_maj < 17) { return false; } require 'MsSetup.inc'; if ($daasMode) { // running against Azure return true; } // if not Azure, check the server version $server_ver = $conn->getAttribute(PDO::ATTR_SERVER_VERSION); if (explode('.', $server_ver)[0] < 13) return false; return true; } /** * Connect to the database specified in MsSetup.inc; Column Encryption keywords automatically added when $keystore is not none * @param string $keywords : string to append to the dsn string in PDO::_construct * @param array $options : attributes to pass to PDO::_construct * @param int $errmode : specifies how the driver reports failures: one of PDO::ERRMODE_EXCEPTION, PDO::ERRMODE_WARNING, or PDO::ERRMODE_SILENT; default is PDO::ERRMODE_EXCEPTION * @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 PDO connection object */ function connect($keywords = '', $options=array(), $errmode = PDO::ERRMODE_EXCEPTION, $disableCE = false) { try { // simply use $databaseName from MsSetup.inc to facilitate testing in Azure, // which does not support switching databases require("MsSetup.inc"); $dsn = getDSN($server, $databaseName, $driver, $keywords, $disableCE); $conn = new PDO($dsn, $uid, $pwd, $options); if ($errmode == PDO::ERRMODE_EXCEPTION || $errmode == PDO::ERRMODE_WARNING || $errmode == PDO::ERRMODE_SILENT) { $conn->setAttribute(PDO::ATTR_ERRMODE, $errmode); } else { printf("connect: The errmode provided must be one of exception, warning, or silent.\n"); } return $conn; } catch (PDOException $e) { var_dump($e->errorInfo); } catch (Exception $e) { var_dump($e->errorInfo); exit; } } /** * @param string $sqlsrvserver : server name * @param string $database : database name * @param string $keywords : string to append to the dsn string in PDO::_construct * @param bool $disableCE : flag for disabling column encryption even when keystore is NOT none * @return string dsn string used for PDO constructor */ function getDSN($sqlsrvserver, $database, $driver = null, $keywords = '', $disableCE = false) { require("MsSetup.inc"); $dsn = ""; if ($sqlsrvserver) { $dsn .= "sqlsrv:Server=$sqlsrvserver;"; } else { printf("getDSN: the sqlsrvserver provided must not be null.\n"); exit; } if ($database) { $dsn .= "database=$database;"; } if (!is_null($driver)) { $dsn .= "driver=$driver;"; } if ($keystore != "none" && !$disableCE) { $dsn .= "ColumnEncryption=Enabled;"; } if ($keystore == "akv" && !$disableCE) { if ($AKVKeyStoreAuthentication == "KeyVaultPassword") { $dsn .= "KeyStoreAuthentication=$AKVKeyStoreAuthentication;KeyStorePrincipalId=$AKVPrincipalName;KeyStoreSecret=$AKVPassword;"; } else if ($AKVKeyStoreAuthentication == "KeyVaultClientSecret") { $dsn .= "KeyStoreAuthentication=$AKVKeyStoreAuthentication;KeyStorePrincipalId=$AKVClientID;KeyStoreSecret=$AKVSecret;"; } } if ($keystore == "ksp" && !$disableCE) { $ksp_path = getKSPPath(); $ksp_name = KSP_NAME; $encrypt_key = ENCRYPT_KEY; $dsn .= "CEKeystoreProvider=$ksp_path;CEKeystoreName=$ksp_name;CEKeystoreEncryptKey=$encrypt_key;"; } if ($keywords) { $dsn .= $keywords; } return $dsn; } /** * @return string CEK name depending on the connection keywords */ function getCekName() { require 'MsSetup.inc'; $cekName = ''; switch ($keystore) { case "none": $cekName = ''; break; case "win": $cekName = 'AEColumnKey'; break; case "ksp": $cekName = 'CustomCEK'; break; case "akv": $cekName = 'AKVColumnKey'; break; default: echo "getCekName: Invalid keystore name.\n"; } return $cekName; } /** * @return the path to the KSP dll/so file */ function getKSPpath() { $name = 'myKSP'; $dir_name = realpath(dirname(__FILE__)); $ksp = $dir_name . DIRECTORY_SEPARATOR . $name; if ( strtoupper( substr( php_uname( 's' ), 0, 3 ) ) == 'WIN' ) { $arch = 'x64'; if ( PHP_INT_SIZE == 4 ) // running 32 bit $arch = ''; $ksp .= $arch . '.dll'; } else $ksp .= '.so'; return $ksp; } /** * 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 $options; //a string that is null by default (e.g. NOT NULL Identity (1,1) ) public $encType; //randomized, deterministic, or none; default is null public $forceEncrypt; //force encryption on a datatype not supported by Column Encrypton public function __construct($dataType, $colName = null, $options = null, $encType = null, $forceEncrypt = false) { if (is_null($colName)) { $this->colName = getDefaultColName($dataType); } else { $this->colName = $colName; } $this->dataType = $dataType; $this->options = $options; if (is_null($encType)) { if (isColEncrypted()) { $this->encType = "deterministic"; } else { $this->encType = "none"; } } else { $this->encType = $encType; } $this->forceEncrypt = $forceEncrypt; } /** * @return string column definition for creating a table */ public function getColDef() { $append = " "; // an identity column is not encrypted because a select query with identity column as the where clause is often run and the user want to have to bind parameter every time if (isColEncrypted() && ($this->encType == "deterministic" || $this->encType == "randomized") && $this->isEncryptableType() && stripos($this->options, "identity") === false && stripos($this->options, "rowguidcol") === false) { $cekName = getCekName(); 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; } /** * @return bool if the datatype for this column is encryptable */ public function isEncryptableType() { $unsupportedTypes = array("money", "smallmoney", "image", "ntext", "text", "xml", "sql_variant", "timestamp", "geography", "geometry", "hierarchyid"); if (!$this->forceEncrypt && in_array(strtolower($this->dataType), $unsupportedTypes)) { return false; } else { return true; } } } /** * @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; } /** * Create a table * @param object $conn : PDO connection object * @param string $tbname : name of the table to be created * @param array $columnMetaArr : array of key value pair with column name as key and datatype as value, or array of columnMeta objects, which contain metadata for one column * @param array $inputs : an associative array column name and its value; value may be a literal value or a ColumnMeta object * @param bool $disableCE : flag for disabling column encryption even when keystore is NOT none * for creating table with datatypes not support for encryption * @param string $tableOption : table options appended to the end of a create table statement * @return int reporting the number of rows affected (should always be 0 for creating table) */ function createTable($conn, $tbname, $columnMetaArr, $tableOption = '', $disableCE = false) { try { dropTable($conn, $tbname); $colDef = ""; foreach ($columnMetaArr as $key => $value) { if (!is_object($value)) { $cm = new ColumnMeta($value, $key); $colDef = $colDef . $cm->getColDef() . ", "; } elseif (get_class($value) == "ColumnMeta") { $colDef = $colDef . $value->getColDef() . ", "; } else { printf("createTable: The input provided must be an associative array of literal values or ColumnMeta objects.\n"); exit; } } $colDef = rtrim($colDef, ", "); $createSql = "CREATE TABLE $tbname ($colDef) $tableOption"; $numRows = $conn->exec($createSql); return $numRows; } catch (PDOException $e) { var_dump($e->errorInfo); } catch (Exception $e) { var_dump($e->errorInfo); exit; } } /** * class for encapsulating optional parameters for PDOStatement::bindParam */ class BindParamOp { public $parameter; //parameter identifier. Parameter name or 1-based index of the parameter public $variable; //name of the PHP variable to bind public $pdoType; //PDO_PARAM_ type specified for PDOStatement::bindParam public $length; //length specified for PDOStatement::bindParam public $options; //options specified for PDOStatement::bindParam public function __construct($parameter, $variable, $pdoType = null, $length = null, $options = null) { $this->parameter = $parameter; $this->variable = $variable; $pdoParams = array("PDO::PARAM_BOOL", "PDO::PARAM_NULL", "PDO::PARAM_INT", "PDO::PARAM_STR", "PDO::PARAM_LOB"); if (in_array($pdoType, $pdoParams) || is_null($pdoType)) { $this->pdoType = $pdoType; } else { printf("BindParamOp construct: The pdoType provided must be one of PDO::PARAM_BOOL, PDO::PARAM_NULL, PDO::PARAM_INT, PDO::PARAM_STR, or PDO::PARAM_LOB.\n"); exit; } if ($length >= 0 || is_null($length)) { $this->length = is_null($length) ? 0 : $length; } else { printf("BindParamOp construct: The length provided must be greater or equal to 0.\n"); exit; } $encodingAttrs = array("PDO::SQLSRV_ENCODING_BINARY", "PDO::SQLSRV_ENCODING_SYSTEM", "PDO::SQLSRV_ENCODING_UTF8", "PDO::SQLSRV_ENCODING_DEFAULT"); if (in_array($options, $encodingAttrs) || is_null($options)) { $this->options = $options; } else { printf("BindParamOp construct: The option provided must be one of PDO::SQLSRV_ENCODING_BINARY, PDO::SQLSRV_ENCODING_SYSTEM, PDO::SQLSRV_ENCODING_UTF8, PDO::SQLSRV_ENCODING_DEFAULT"); } } /** * @param object $stmt : PDO Statement object */ public function bindWithOp($stmt) { // get the constant values of pdoType, and option $pdoType = null; $options = null; if (!is_null($this->pdoType)) { $pdoType = constant($this->pdoType); } if (!is_null($this->options)) { $options = constant($this->options); } $stmt->bindParam($this->parameter, $this->variable, $pdoType, $this->length, $options); } } /** * Insert a row into a table * @param object $conn : PDO connection object * @param string $tbname : name of the table for the row to be inserted * @param array $inputs : an associative array column name and its value; value may be a literal value or a BindParamOp object * @param string $api : PDO_SQLSRV API used for executing the insert query * accepted values: "exec", "query", "prepareDirect", "prepareExecuteBind", "prepareBindParam" * @param bool $r : true if the row was successfully inserted, otherwise false. Default value is null to make this parameter optional. * @return object PDOStatement object of the insert statement */ function insertRow($conn, $tbname, $inputs, $api = null, &$r = null) { try { // set the default API being called depending on if column is encrypted if (is_null($api)) { if (!isColEncrypted()) { $api = "query"; } else { $api = "prepareBindParam"; } } $stmt = null; if (!isColEncrypted() && $api != "prepareExecuteBind" && $api != "prepareBindParam") { $insertSql = getInsertSqlComplete($tbname, $inputs); switch ($api) { case "exec": $conn->exec($insertSql); break; case "query": $stmt = $conn->query($insertSql); break; case "prepareDirect": $stmt = $conn->prepare($insertSql); $r = $stmt->execute(); break; } } else { // if AE is on, must bind param $insertSql = getInsertSqlPlaceholders($tbname, $inputs); $stmt = $conn->prepare($insertSql); if ($api == "prepareExecuteBind") { $params = array_values($inputs); $r = $stmt->execute($params); } else { $i = 1; foreach ($inputs as $key => $value) { if (!is_object($value)) { $stmt->bindParam($i, $inputs[$key]); } elseif (get_class($value) == "BindParamOp") { $value->bindWithOp($stmt); } else { printf("insertRow: The inputs provided must be a literal value or a BindParamOp object.\n"); exit; } $i++; } $r = $stmt->execute(); } } return $stmt; } catch (PDOException $e) { var_dump($e->errorInfo); } catch (Exception $e) { var_dump($e->errorInfo); exit; } } /** * @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 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 $input : 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) { if (empty($inputs)) { echo "getInsertSqlPlaceholders: inputs for inserting a row cannot be empty.\n"; return ""; } $colStr = "INSERT INTO $tbname ("; $valStr = "VALUES ("; 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; } /** * Compare two floating point numbers and return true if the difference is minimal * @param float $expected : expected value * @param float $actual : actual value */ function compareFloats($expected, $actual) { $epsilon = 0.00001; $diff = abs(($actual - $expected) / $expected); return ($diff < $epsilon); } /** * Fetch all rows and all columns given a table name, and print them * @param object $conn : PDO connection object * @param string $tbname : name of the table to fetch from */ function fetchAll($conn, $tbname) { try { $sql = "SELECT * FROM $tbname"; $stmt = $conn->query($sql); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { foreach ($row as $key => $value) { print("$key: $value\n"); } } } catch (PDOException $e) { var_dump($e->errorInfo); } catch (Exception $e) { var_dump($e->errorInfo); exit; } } /** * Use PDOStatement::fetchAll to fetch all rows given a table name * @param object $conn : PDO connection object * @param string $tbname : name of the table to fetch from * @param string $fetchStyle : fetch_style argument passed to PDOStatement::fetchAll * @return array rows in a result set */ function selectAll($conn, $tbname, $fetchStyle = PDO::FETCH_BOTH) { try { $sql = "SELECT * FROM $tbname"; $stmt = $conn->query($sql); $data = $stmt->fetchAll($fetchStyle); return $data; } catch (PDOException $e) { var_dump($e->errorInfo); } catch (Exception $e) { var_dump($e->errorInfo); exit; } } /** * Use PDOStatement::fetch to fetch a row given a table name * @param object $conn : PDO connection object * @param string $tbname : name of the table to fetch from * @param string $fetchStyle : fetch_style argument passed to PDOStatement::fetch * @return array a row from the result set */ function selectRow($conn, $tbname, $fetchStyle = null) { try { $sql = "SELECT * FROM $tbname"; $stmt = $conn->query($sql); $row = $stmt->fetch(constant($fetchStyle)); return $row; } catch (PDOException $e) { var_dump($e->errorInfo); } catch (Exception $e) { var_dump($e->errorInfo); exit; } } /** * @return bool false if $keystore specified in MsSetup.inc is none, otherwise return true */ function isColEncrypted() { require 'MsSetup.inc'; if ($keystore == "none" || !$dataEncrypted) { return false; } else { return true; } } function isAEConnected() { require('MsSetup.inc'); return $keystore != "none"; } function teardown() { // TBD } function my_print_r($to_print) { global $verbose; if ($verbose) { print_r($to_print); } } function TestMode() { $testMode = getenv('PHPT_EXEC'); return ($testMode ? true : false); } function IsPdoMode() { require 'MsSetup.inc'; return (($driverType === true) ? true : false); } function TraceMode() { require 'MsSetup.inc'; return ((!TestMode() && $traceEnabled) ? true : false); } function IsMarsSupported() { require 'MsSetup.inc'; return ($marsMode ? true : false); } function IsDaasMode() { require 'MsSetup.inc'; return ($daasMode ? true : false); } function isServerHGSEnabled() { $enabled = false; try { $conn = connect(); $tsql = "SELECT @@SERVERNAME"; $stmt = $conn->query($tsql); $result = $stmt->fetch(PDO::FETCH_NUM); $name = $result[0]; $enabled = (strpos($result[0], 'HGS') != false); } catch (Exception $e) { echo $e->getMessage(); die("Could not fetch server property."); } return $enabled; } function isSQLAzure() { // 'SQL Azure' indicates SQL Database or SQL Data Warehouse // For details, https://docs.microsoft.com/sql/t-sql/functions/serverproperty-transact-sql try { $conn = connect(); $tsql = "SELECT SERVERPROPERTY ('edition')"; $stmt = $conn->query($tsql); $result = $stmt->fetch(PDO::FETCH_NUM); $edition = $result[0]; if ($edition === "SQL Azure") { return true; } else { return false; } } catch (Exception $e) { echo $e->getMessage(); die("Could not fetch server property."); } } function isAzureDW() { // Check if running Azure Data Warehouse // For details, https://docs.microsoft.com/sql/t-sql/functions/serverproperty-transact-sql try { $conn = connect(); // Check if running Azure Data Warehouse // For details, https://docs.microsoft.com/sql/t-sql/functions/serverproperty-transact-sql $tsql = "SELECT SERVERPROPERTY ('edition'), SERVERPROPERTY ('EngineEdition')"; $stmt = $conn->query($tsql); $result = $stmt->fetch(PDO::FETCH_NUM); $edition = $result[0]; $engEd = intval($result[1]); if ($edition == "SQL Azure" && $engEd == 6) { return true; } else { return false; } } catch (Exception $e) { echo $e->getMessage(); die("skip Failed to connect or could not fetch edition info."); } } function FatalError($errorMsg) { if (!IsPdoMode()) { handle_errors(); } die("$errorMsg\n"); } function Trace($msg) { if (TraceMode()) { echo $msg; } } function TraceEx($msg, $exitMode) { if ($exitMode) { FatalError($msg); } else { Trace("$msg\n"); } } function TraceData($sqlType, $data) { if (TraceMode()) { $msg = strtoupper(" $sqlType:"); echo "$msg\t"; if (strlen($msg) <= 7) { echo "\t"; } if (strlen($msg) <= 15) { echo "\t"; } echo "$data\n" ; } } function StartTest($testName) { require 'MsSetup.inc'; if (TraceMode()) { echo "$PhpDriver: starting \"$testName\" test...\n\n"; } if (!extension_loaded(IsPdoMode() ? "pdo" : "sqlsrv")) { die("$PhpDriver cannot be loaded."); } // Set timezone $tz = ini_get('date.timezone'); if (strcmp($tz, "") == 0) { date_default_timezone_set('America/Los_Angeles'); $tz = date_default_timezone_get(); } Trace("Timezone: $tz.\n"); } function EndTest($testName) { require 'MsSetup.inc'; if (TraceMode()) { echo "\n$PhpDriver: " ; } echo "Test \"$testName\" completed successfully.\n" ; } function Setup() { set_time_limit(0); if (IsPdoMode()) { // PDO setup } else { // PHP setup sqlsrv_configure('LogSubsystems', SQLSRV_LOG_SYSTEM_ALL); sqlsrv_configure('LogSeverity', SQLSRV_LOG_SEVERITY_ALL); sqlsrv_configure('WarningsReturnAsErrors', 1); } } function Configure($param, $expected) { if (!IsPdoMode()) { sqlsrv_configure($param, $expected); $actual = sqlsrv_get_config($param); if ($actual == $expected) { Trace("Set configuration parameter $param = $actual.\n"); } else { die("Failed to set configuration parameter $param = $expected."); } } } function getTableName($table = '') { $timestamp = round(microtime(true)*1000); if (strlen($table) == 0) { $table = 'php_test_table'; } return $table . '_' . $timestamp; } function getProcName($proc = '') { // A temporary stored procedure name with the '#' prefix will be // automatically dropped once the connection is closed. Otherwise, // the caller should take care of dropping the temp procedure afterwards. $timestamp = round(microtime(true)*1000); if (strlen($proc) == 0) { $proc = 'php_test_proc'; } return $proc . '_' . $timestamp; } function PDOConnect($className, $serverName, $user, $pwd, $exitMode) { require 'MsSetup.inc'; $conn = null; try { // simply use $databaseName from MsSetup.inc to facilitate testing in Azure, // which does not support switching databases $conn = new $className("sqlsrv:Server=$serverName;Database=$databaseName", $user, $pwd, $connectionOptions); $conn->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_SYSTEM); } catch (PDOException $e) { $conn = null; TraceEx("\nFailed to connect to $serverName: ".$e->getMessage(), $exitMode); } return ($conn); } function ExecuteQuery($conn, $tsql) { $stmt = null; if (IsPdoMode()) { // PDO try { $stmt = $conn->query($tsql); } catch (PDOException $e) { $stmt = null; FatalError("Query execution failed for $tsql: ".$e->getMessage()); } } else { // PHP $stmt = sqlsrv_query($conn, $tsql); if ($stmt === false) { FatalError("Query execution failed for $tsql"); } } return ($stmt); } function PrepareQuery($conn, $tsql) { $stmt = null; if (IsPdoMode()) { // PDO try { $stmt = $conn->prepare($tsql); } catch (PDOException $e) { $stmt = null; FatalError("Query preparation failed for $tsql: ".$e->getMessage()); } } else { // PHP $stmt = sqlsrv_prepare($conn, $tsql); if ($stmt === false) { FatalError("Query preparation failed for $tsql"); } } return ($stmt); } function ExecuteQueryEx($conn, $tsql, $modeDirect) { $stmt = null; if ($modeDirect) { // direct execution if (IsPdoMode()) { // PDO try { $stmt = $conn->query($tsql); } catch (PDOException $e) { $stmt = null; } } else { // PHP $stmt = sqlsrv_query($conn, $tsql); } } else { $stmt = PrepareQuery($conn, $tsql); if (IsPdoMode()) { $stmt->execute(); } else { sqlsrv_execute($stmt); } } return ($stmt); } 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 GetDriverType($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 IsStreamable($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 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) { switch ($k) { case 27: return (false); // timestamp default: break; } return (true); } 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: break; } return (""); } 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 (""); } 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 ("The quick brown fox jumps over the lazy dog0123456789"); default: break; } return (null); } function CreateDB($conn, $dbName) { Trace("Creating database $dbName ..."); $tsql = "CREATE DATABASE [$dbName]"; DropDB($conn, $dbName); if (IsPdoMode()) { $outcome = $conn->exec($tsql); if ($outcome === false) { FatalError("Failed to create test database: $dbName"); } } else { $stmt = sqlsrv_query($conn, $tsql); if ($stmt === false) { FatalError("Failed to create test database: $dbName"); } sqlsrv_free_stmt($stmt); } Trace(" completed successfully.\n"); } function DropDB($conn, $dbName) { $tsql = "DROP DATABASE [$dbName]"; if (IsPdoMode()) { $mode = $conn->getAttribute(PDO::ATTR_ERRMODE); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); $outcome = $conn->exec($tsql); $conn->setAttribute(PDO::ATTR_ERRMODE, $mode); } else { $stmt = sqlsrv_query($conn, $tsql); if ($stmt === false) { } else { sqlsrv_free_stmt($stmt); } } } /* function CreateTable($conn, $tableName) { $dataType = "[c1_int] int, [c2_tinyint] tinyint, [c3_smallint] smallint, [c4_bigint] bigint, [c5_bit] bit, [c6_float] float, [c7_real] real, [c8_decimal] decimal(28,4), [c9_numeric] numeric(32,4), [c10_money] money, [c11_smallmoney] smallmoney, [c12_char] char(512), [c13_varchar] varchar(512), [c14_varchar_max] varchar(max), [c15_nchar] nchar(512), [c16_nvarchar] nvarchar(512), [c17_nvarchar_max] nvarchar(max), [c18_text] text, [c19_ntext] ntext, [c20_binary] binary(512), [c21_varbinary] varbinary(512), [c22_varbinary_max] varbinary(max), [c23_image] image, [c24_uniqueidentifier] uniqueidentifier, [c25_datetime] datetime, [c26_smalldatetime] smalldatetime, [c27_timestamp] timestamp, [c28_xml] xml"; CreateTableEx($conn, $tableName, $dataType); if (IsDaasMode()) { $colIndex = "[c1_int], [c2_tinyint], [c3_smallint], [c4_bigint], [c5_bit], [c6_float], [c7_real], [c8_decimal], [c9_numeric]"; CreateTableIndex($conn, $tableName, $colIndex); } } */ function CreateTableEx($conn, $tableName, $dataType) { Trace("Creating table $tableName ..."); $tsql = "CREATE TABLE [$tableName] ($dataType)"; dropTable($conn, $tableName); if (IsPdoMode()) { $outcome = $conn->exec($tsql); if ($outcome === false) { FatalError("Failed to create test table: $tsql"); } } else { $stmt = sqlsrv_query($conn, $tsql); if ($stmt === false) { FatalError("Failed to create test table: $tsql"); } sqlsrv_free_stmt($stmt); } Trace(" completed successfully.\n"); } function CreateTableIndex($conn, $tableName, $colIndex) { require 'MsSetup.inc'; CreateTableIndexEx($conn, $tableName, $tableIndex, $colIndex); } function CreateTableIndexEx($conn, $tableName, $tableIndex, $colIndex) { Trace("Creating table index for $tableName ..."); $sqlIndex = "CREATE CLUSTERED INDEX [$tableIndex] ON [$tableName]($colIndex)"; if (IsPdoMode()) { $outcome = $conn->exec($sqlIndex); if ($outcome === false) { FatalError("Failed to create clustered index for test table: $sqlIndex"); } } else { $stmt = sqlsrv_query($conn, $sqlIndex); if ($stmt === false) { FatalError("Failed to create clustered index for test table: $sqlIndex"); } sqlsrv_free_stmt($stmt); } Trace(" completed successfully.\n"); } function CreateUniqueIndex($conn, $tableName, $colIndex) { require 'MsSetup.inc'; CreateUniqueIndexEx($conn, $tableName, $tableIndex, $colIndex); } function CreateUniqueIndexEx($conn, $tableName, $tableIndex, $colIndex) { Trace("Creating unique table index for $tableName ..."); $sqlIndex = "CREATE UNIQUE INDEX [$tableIndex] ON [$tableName]($colIndex)"; if (IsPdoMode()) { $outcome = $conn->exec($sqlIndex); if ($outcome === false) { FatalError("Failed to create unique index for test table: $sqlIndex"); } } else { $stmt = sqlsrv_query($conn, $sqlIndex); if ($stmt === false) { FatalError("Failed to create unique index for test table: $sqlIndex"); } sqlsrv_free_stmt($stmt); } Trace(" completed successfully.\n"); } function dropTable($conn, $tableName) { $tsql = "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'" . $tableName . "') AND type in (N'U')) DROP TABLE $tableName"; if (IsPdoMode()) { $mode = $conn->getAttribute(PDO::ATTR_ERRMODE); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); $outcome = $conn->exec($tsql); $conn->setAttribute(PDO::ATTR_ERRMODE, $mode); } else { $stmt = sqlsrv_query($conn, $tsql); if ($stmt === false) { } else { sqlsrv_free_stmt($stmt); } } } function SelectFromTable($conn, $tableName) { return (SelectFromTableEx($conn, $tableName, null)); } function SelectFromTableEx($conn, $tableName, $cond) { if (($cond != null) && (strlen($cond) > 0)) { return (SelectQuery($conn, "SELECT * FROM [$tableName] WHERE $cond")); } else { return (SelectQuery($conn, "SELECT * FROM [$tableName]")); } } function SelectQuery($conn, $query) { return (SelectQueryEx($conn, $query, null)); } function SelectQueryEx($conn, $query, $options) { $numFields = 0; if (IsPDOMode()) { $stmt = ExecuteQuery($conn, $query); $numFields = $stmt->columnCount(); } else { if ($options != null) { $stmt = sqlsrv_query($conn, $query, null, $options); } else { $stmt = sqlsrv_query($conn, $query); } if ($stmt === false) { FatalError("Failed to query test table"); } $numFields = sqlsrv_num_fields($stmt); } if ($numFields <= 0) { die("Unexpected number of fields: .$numFields"); } return ($stmt); } function RowCount($stmt) { $rowCount = 0; if (IsPdoMode()) { while ($stmt->fetchColumn()) { $rowCount++; } } else { while (sqlsrv_fetch($stmt)) { $rowCount++; } } return ($rowCount); } function NumRows($conn, $tableName) { $stmt = SelectFromTable($conn, $tableName); $rowCount = RowCount($stmt); if (IsPdoMode()) { $stmt = null; } else { sqlsrv_free_stmt($stmt); } return ($rowCount); } function InsertQuery($tableName) { include_once 'MsData_UTF8.inc'; return (InsertQueryExUTF8($tableName, rand(1, 20))); } function InsertRows($conn, $tableName, $rowCount) { Trace("Inserting $rowCount rows into $tableName ..."); $count = 0; for ($i = 0; $i < $rowCount; $i++) { if (InsertRow($conn, $tableName)) { $count++; } } Trace(" completed successfully.\n"); if ($count != $rowCount) { die("$count rows inserted instead of $rowCount\n"); } return ($count); } function InsertRowsByRange($conn, $tableName, $minIndex, $maxIndex) { $rowCount = $maxIndex - $minIndex + 1; if ($rowCount > 0) { Trace("Inserting $rowCount rows into $tableName ..."); for ($i = $minIndex; $i <= $maxIndex; $i++) { InsertRowByIndex($conn, $tableName, $i); } Trace(" completed successfully.\n"); } } /* function InsertRow($conn, $tableName) { $tsql = InsertQuery($tableName); $stmt = null; if (IsPdoMode()) { $stmt = $conn->exec($tsql); } else { $stmt = sqlsrv_query($conn, $tsql); } return (InsertCheck($stmt)); } */ function InsertRowEx($conn, $tableName, $dataCols, $dataValues, $dataOptions) { $tsql = "INSERT INTO [$tableName] ($dataCols) VALUES ($dataValues)"; $stmt = null; if (IsPdoMode()) { $stmt = $conn->exec($tsql); } else { $stmt = sqlsrv_query($conn, $tsql, $dataOptions); } return (InsertCheck($stmt)); } function InsertRowByIndex($conn, $tableName, $index) { $tsql = InsertQueryEx($tableName, $index); $stmt = null; if (IsPdoMode()) { $stmt = $conn->exec($tsql); } else { $stmt = sqlsrv_query($conn, $tsql); } return (InsertCheck($stmt)); } function InsertStream($conn, $tableName, $dataCols, $dataValues, $dataOptions, $atExec) { $tsql = "INSERT INTO [$tableName] ($dataCols) VALUES ($dataValues)"; $stmt = null; if (IsPdoMode()) { $stmt = $conn->exec($tsql); } else { if ($atExec) { $stmt = sqlsrv_query($conn, $tsql, $dataOptions, array('SendStreamParamsAtExec' => 1)); } else { $stmt = sqlsrv_query($conn, $tsql, $dataOptions); if ($stmt) { while (sqlsrv_send_stream_data($stmt)) { } } } } return (InsertCheck($stmt)); } function InsertCheck($stmt) { $numRows = 0; if ($stmt === false) { FatalError("Failed to insert row into test table"); } if (IsPdoMode()) { $numRows = $stmt; } else { $numRows = sqlsrv_rows_affected($stmt); sqlsrv_free_stmt($stmt); } if ($numRows != 1) { die("Unexpected row count at insert: ".$numRows); } return (true); } function GetInsertData($rowIndex, $colIndex, $skip) { $query = InsertQueryEx("TestTable", $rowIndex); $data = strstr($query, "(("); $pos = 1; if ($data === false) { die("Failed to retrieve data on row $rowIndex"); } $data = substr($data, 2); while ($pos < ($colIndex - $skip)) { $data = strstr($data, ", ("); $pos++; if ($data === false) { die("Failed to retrieve data on row $rowIndex, column $pos"); } $data = substr($data, 3); } $pos = strpos($data, ")"); if ($pos === false) { die("Failed to isolate data on row $rowIndex, column $pos"); } $data = substr($data, 0, $pos); if (strcasecmp($data, "null") == 0) { $data = ""; } if (IsUnicode($colIndex)) { // N'data' $data = substr($data, 2, strlen($data) - 3); } elseif (IsLiteral($colIndex)) { // 'data' $data = substr($data, 1, strlen($data) - 2); } elseif (IsBinary($colIndex)) { // 0xdata $data = substr($data, 2); } return (trim($data)); } function CreateProc($conn, $procName, $procArgs, $procCode) { DropProc($conn, $procName); if (!IsPdoMode()) { $stmt = sqlsrv_query($conn, "CREATE PROC [$procName] ($procArgs) AS BEGIN $procCode END"); if ($stmt === false) { FatalError("Failed to create test procedure"); } sqlsrv_free_stmt($stmt); } else { $stmt = $conn->query("CREATE PROC [$procName] ($procArgs) AS BEGIN $procCode END"); } } function dropProc($conn, $procName) { if (!IsPdoMode()) { $stmt = sqlsrv_query($conn, "DROP PROC [$procName]"); sqlsrv_free_stmt($stmt); } else { $query = "IF OBJECT_ID('[$procName]', 'P') IS NOT NULL DROP PROCEDURE [$procName]"; $stmt = $conn->query($query); } } function CallProc($conn, $procName, $procArgs, $procValues) { $stmt = CallProcEx($conn, $procName, "", $procArgs, $procValues); sqlsrv_free_stmt($stmt); } function CallProcEx($conn, $procName, $procPrefix, $procArgs, $procValues) { $stmt = sqlsrv_query($conn, "{ $procPrefix CALL [$procName] ($procArgs)}", $procValues); if ($stmt === false) { FatalError("Failed to call test procedure"); } return ($stmt); } function CreateFunc($conn, $funcName, $funcArgs, $retType, $funcCode) { DropFunc($conn, $funcName); try { $stmt = $conn->query("CREATE FUNCTION [$funcName] ($funcArgs) RETURNS $retType AS BEGIN $funcCode END"); } catch (PDOException $e) { echo "Failed to create test function\n"; var_dump($e); } unset($stmt); } function DropFunc($conn, $funcName) { try { $conn->query("DROP FUNCTION [$funcName]"); } catch (PDOException $e) { ; // do nothing } } function CallFunc($conn, $funcName, $funcArgs, $funcValues) { $stmt = sqlsrv_query($conn, "{ ? = CALL [$funcName]($funcArgs)}", $funcValues); if ($stmt === false) { FatalError("Failed to call test function"); } sqlsrv_free_stmt($stmt); } function handle_errors() { $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); $count = count($errors); if ($count == 0) { $errors = sqlsrv_errors(SQLSRV_ERR_ALL); $count = count($errors); } if ($count > 0) { for ($i = 0; $i < $count; $i++) { Trace($errors[$i]['message']."\n"); } } } function str2hex($dataIn) { $dataOut = ""; $len = strlen($dataIn); for ($i = 0; $i < $len; $i++) { $ch = strtoupper(substr($dataIn, $i, 1)); if ($ch == "A") { $dataOut = $dataOut."41"; } elseif ($ch == "B") { $dataOut = $dataOut."42"; } elseif ($ch == "C") { $dataOut = $dataOut."43"; } elseif ($ch == "D") { $dataOut = $dataOut."44"; } elseif ($ch == "E") { $dataOut = $dataOut."45"; } elseif ($ch == "F") { $dataOut = $dataOut."46"; } else { $dataOut = $dataOut."3".$ch; } } return ($dataOut); } function PhpVersionComponents(&$major, &$minor, &$sub) { $str_version = phpversion(); $major = strtok($str_version, "."); $minor = strtok("."); $sub = strtok("."); } function getTodayDateAsString($conn) { $tsql = 'SELECT CONVERT (VARCHAR(20), GETDATE())'; $stmt = $conn->query($tsql); $row = $stmt->fetch(PDO::FETCH_NUM); return $row[0]; } function compareResourceToInput($actual, $expected) { $size = 8192; $pos = 0; $matched = true; while (!feof($actual)) { $original = fread($actual, $size); $str = substr($expected, $pos, $size); if ($original !== $str) { $matched = false; break; } $pos += $size; } return $matched; }