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 ("The quick brown fox jumps over the lazy dog0123456789"); default: break; } return (null); } } ?>