diff --git a/classes/db/DBMssql.class.php b/classes/db/DBMssql.class.php index 99ac2677a..1f77388f0 100644 --- a/classes/db/DBMssql.class.php +++ b/classes/db/DBMssql.class.php @@ -3,7 +3,7 @@ /** * @class DBMSSQL * @author zero (zero@nzeo.com) - * @brief MSSQL 을 ADODB로 이 + * @brief MSSQL driver로 수정 sol (sol@ngleader.com) * @version 0.1 **/ @@ -12,10 +12,10 @@ /** * DB를 이용하기 위한 정보 **/ - var $conn = NULL; - var $rs = NULL; - var $database = NULL; ///< database - var $prefix = 'xe'; ///< XE에서 사용할 테이블들의 prefix (한 DB에서 여러개의 XE 설치 가능) + var $conn = NULL; + var $database = NULL; ///< database + var $prefix = 'xe'; ///< XE에서 사용할 테이블들의 prefix (한 DB에서 여러개의 XE 설치 가능) + var $param = array(); /** * @brief mssql 에서 사용될 column type @@ -46,8 +46,7 @@ * @brief 설치 가능 여부를 return **/ function isSupported() { - return false; - if(!class_exists('COM')) return false; + if (!extension_loaded("sqlsrv")) return false; return true; } @@ -73,22 +72,31 @@ // db 정보가 없으면 무시 if(!$this->hostname || !$this->database) return; - $this->conn = new COM("ADODB.Connection",NULL,CP_UTF8); - //$this->conn = new COM("ADODB.Connection"); - $this->conn->open( sprintf('Provider=sqloledb;Data Source=%s;Initial Catalog=%s;Network Library=dbmssocn;User ID=%s;Password=%s;', $this->hostname, $this->database, $this->userid, $this->password)); - - // 접속체크 - $this->is_connected = true; + //sqlsrv_configure( 'WarningsReturnAsErrors', 0 ); + //sqlsrv_configure( 'LogSeverity', SQLSRV_LOG_SEVERITY_ALL ); + //sqlsrv_configure( 'LogSubsystems', SQLSRV_LOG_SYSTEM_ALL ); + + $this->conn = sqlsrv_connect( $this->hostname, + array( 'Database' => $this->database,'UID'=>$this->userid,'PWD'=>$this->password )); + + + // 접속체크 + if($this->conn){ + $this->is_connected = true; + }else{ + $this->is_connected = false; + } } /** * @brief DB접속 해제 **/ function close() { - if(!$this->isConnected()) return; + if($this->is_connected == false) return; + $this->commit(); - $this->conn->close(); - $this->rs = $this->conn = null; + sqlsrv_close($this->conn); + $this->conn = null; } /** @@ -96,7 +104,8 @@ **/ function addQuotes($string) { if(version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); - if(!is_numeric($string)) $string = str_replace("'","''",$string); + //if(!is_numeric($string)) $string = str_replace("'","''",$string); + return $string; } @@ -104,30 +113,30 @@ * @brief 트랜잭션 시작 **/ function begin() { - return; - if(!$this->isConnected() || $this->transaction_started) return; + if($this->is_connected == false || $this->transaction_started) return; + if(sqlsrv_begin_transaction( $this->conn ) === false) return; + $this->transaction_started = true; - $this->_query("BEGIN TRANSACTION XE_Transaction"); } /** * @brief 롤백 **/ function rollback() { - return; - if(!$this->isConnected() || !$this->transaction_started) return; - $this->transaction_started = false; - $this->_query("ROLLBACK TRANSACTION XE_Transaction"); + if($this->is_connected == false || !$this->transaction_started) return; + + $this->transaction_started = false; + sqlsrv_rollback( $this->conn ); } /** * @brief 커밋 **/ function commit($force = false) { - return; - if(!$force && (!$this->isConnected() || !$this->transaction_started)) return; + if(!$force && ($this->is_connected == false || !$this->transaction_started)) return; + $this->transaction_started = false; - $this->_query("COMMIT TRANSACTION XE_Transaction"); + sqlsrv_commit( $this->conn ); } /** @@ -140,47 +149,63 @@ * return\n **/ function _query($query) { - if(!$this->isConnected() || !$query) return; - - - $this->rs = new COM("ADODB.Recordset"); - $this->rs->CursorLocation=3; + if($this->is_connected == false || !$query) return; + $_param = array(); + + if(count($this->param)){ + foreach($this->param as $k => $o){ + if($o['type'] == 'number'){ + $_param[] = &$o['value']; + }else{ + $_param[] = array(&$o['value'], SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('utf-8')); + } + } + } + // 쿼리 시작을 알림 $this->actStart($query); - - // 쿼리 문 실행 - //try { - //@$this->rs->open($query,$this->conn,0,1,1); - //} catch(Exception $e) { - //$this->setError('MSSQL Error in '.$query); - //} + // 쿼리 문 실행 + $result = false; + if(count($_param)){ + $result = @sqlsrv_query($this->conn, $query, $_param); + }else{ + $result = @sqlsrv_query($this->conn, $query); + } + // 오류 체크 + if(!$result) $this->setError(print_r(sqlsrv_errors())); + // 쿼리 실행 종료를 알림 $this->actFinish(); + $this->param = array(); + + return $result; } /** * @brief 결과를 fetch **/ - function _fetch() { - if(!$this->isConnected() || $this->isError() || !$this->rs) return; + function _fetch($result) { + if(!$this->isConnected() || $this->isError() || !$result) return; + $c = sqlsrv_num_fields($result); + $m = null; $output = array(); - $k = (int)$this->rs->Fields->Count; - for($i=0;!$this->rs->EOF;$this->rs->MoveNext(),$i++){ + + while(sqlsrv_fetch($result)){ + if(!$m) $m = sqlsrv_field_metadata($result); unset($row); - for($j=0;$j<$k;$j++){ - $row->{$this->rs[$j]->name} = $this->rs[$j]->value; - } - $output[$i]=$row; - } - $this->rs->close(); - $this->rs = null; - - if(count($output)==1) return $output[0]; + for($i=0;$i<$c;$i++){ + $row->{$m[$i]['Name']} = sqlsrv_get_field( $result, $i, SQLSRV_PHPTYPE_STRING( 'utf-8' )); + } + $output[] = $row; + } + + if(count($output)==1) return $output[0]; return $output; + } /** @@ -189,9 +214,12 @@ function getNextSequence() { $query = sprintf("insert into %ssequence (seq) values (ident_incr('%ssequence'))", $this->prefix, $this->prefix); $this->_query($query); + $query = sprintf("select ident_current('%ssequence')+1 as sequence", $this->prefix); - $this->_query($query); - $tmp = $this->_fetch(); + $result = $this->_query($query); + $tmp = $this->_fetch($result); + + return $tmp->sequence; } @@ -200,8 +228,9 @@ **/ function isTableExists($target_name) { $query = sprintf("select name from sysobjects where name = '%s%s' and xtype='U'", $this->prefix, $this->addQuotes($target_name)); - $this->_query($query); - $tmp = $this->_fetch(); + $result = $this->_query($query); + $tmp = $this->_fetch($result); + if(!$tmp) return false; return true; } @@ -237,9 +266,9 @@ **/ function isColumnExists($table_name, $column_name) { $query = sprintf("select syscolumns.name as name from syscolumns, sysobjects where sysobjects.name = '%s%s' and sysobjects.id = syscolumns.id and syscolumns.name = '%s'", $this->prefix, $table_name, $column_name); - $this->_query($query); + $result = $this->_query($query); if($this->isError()) return; - $tmp = $this->_fetch(); + $tmp = $this->_fetch($result); if(!$tmp->name) return false; return true; } @@ -272,9 +301,9 @@ function isIndexExists($table_name, $index_name) { $query = sprintf("select sysindexes.name as name from sysindexes, sysobjects where sysobjects.name = '%s%s' and sysobjects.id = sysindexes.id and sysindexes.name = '%s'", $this->prefix, $table_name, $index_name); - $this->_query($query); + $result = $this->_query($query); if($this->isError()) return; - $tmp = $this->_fetch(); + $tmp = $this->_fetch($result); if(!$tmp->name) return false; return true; @@ -386,6 +415,7 @@ function _getCondition($conditions,$column_type) { $condition = ''; + foreach($conditions as $val) { $sub_condition = ''; foreach($val['condition'] as $v) { @@ -397,6 +427,7 @@ if(preg_match('/^substr\(/i',$name)) $name = preg_replace('/^substr\(/i','substring(',$name); $operation = $v['operation']; $value = $v['value']; + $type = $this->getColumnType($column_type,$name); $pipe = $v['pipe']; @@ -414,6 +445,75 @@ return $condition; } + + function getConditionValue($name, $value, $operation, $type, $column_type) { + + if($type == 'number') { + if(strpos($value,',')===false && strpos($value,'(')===false){ + + if(is_integer($value)){ + $this->param[] = array('type'=>'number','value'=>(int)$value); + return '?'; + }else{ + return $value; + } + } + } + + if(strpos($name,'.')!==false&&strpos($value,'.')!==false) { + list($table_name, $column_name) = explode('.',$value); + if($column_type[$column_name]){ + return $value; + } + } + + switch($operation) { + case 'like_prefix' : + $value = preg_replace('/(^\'|\'$){1}/','',$value); + $this->param[] = array('type'=>$column_type[$name],'value'=>$value); + + $value = "? + '%'"; + break; + case 'like_tail' : + $value = preg_replace('/(^\'|\'$){1}/','',$value); + $this->param[] = array('type'=>$column_type[$name],'value'=>$value); + + $value = "'%' + ?"; + break; + case 'like' : + $value = preg_replace('/(^\'|\'$){1}/','',$value); + $this->param[] = array('type'=>$column_type[$name],'value'=>$value); + + $value = "'%' + ? + '%'"; + break; + case 'notin' : + preg_match_all('/,?\'([^\']*)\'/',$value,$match); + $val = array(); + foreach($match[1] as $k => $v){ + $this->param[] = array('type'=>$column_type[$name],'value'=>trim($v)); + $val[] ='?'; + } + $value = join(',',$val); + break; + case 'in' : + preg_match_all('/,?\'([^\']*)\'/',$value,$match); + $val = array(); + foreach($match[1] as $k => $v){ + $this->param[] = array('type'=>$column_type[$name],'value'=>trim($v)); + $val[] ='?'; + } + $value = join(',',$val); + break; + default: + $value = preg_replace('/(^\'|\'$){1}/','',$value); + $this->param[] = array('type'=>$column_type[$name],'value'=>$value); + $value = '?'; + break; + } + + return $value; + } + /** * @brief insertAct 처리 **/ @@ -428,13 +528,21 @@ foreach($output->columns as $key => $val) { $name = $val['name']; $value = $val['value']; + if($output->column_type[$name]!='number') { - $value = "'".$this->addQuotes($value)."'"; - if(!$value) $value = 'null'; - } elseif(!$value || is_numeric($value)) $value = (int)$value; - + $value = $this->addQuotes($value); + if(!$value) $value = ''; + } elseif(is_numeric($value)){ + if(!$value) $value = ''; + $value = (int)$value; + } elseif(!$value){ + $value = ''; + } + $column_list[] = '['.$name.']'; - $value_list[] = $value; + $value_list[] = '?'; + + $this->param[] = array('type'=>$output->column_type[$name], 'value'=>$value); } $query = sprintf("insert into %s (%s) values (%s);", implode(',',$table_list), implode(',',$column_list), implode(',', $value_list)); @@ -450,18 +558,33 @@ foreach($output->tables as $key => $val) { $table_list[] = '['.$this->prefix.$val.']'; } - - // 컬럼 정리 + + // 컬럼 정리 foreach($output->columns as $key => $val) { if(!isset($val['value'])) continue; + $name = $val['name']; $value = $val['value']; - if(strpos($name,'.')!==false&&strpos($value,'.')!==false) $column_list[] = $name.' = '.$value; - else { - if($output->column_type[$name]!='number') $value = "'".$this->addQuotes($value)."'"; - elseif(!$value || is_numeric($value)) $value = (int)$value; + if(strpos($name,'.')!==false&&strpos($value,'.')!==false){ + $column_list[] = $name.' = '.$value; + } else { + if($output->column_type[$name]!='number'){ + $value = $this->addQuotes($value); + if(!$value) $value = ''; + + $this->param[] = array('type'=>$output->column_type[$name], 'value'=>$value); + $column_list[] = sprintf("[%s] = ?", $name); + }elseif(!$value || is_numeric($value)){ + $value = (int)$value; + + $this->param[] = array('type'=>$output->column_type[$name], 'value'=>$value); + $column_list[] = sprintf("[%s] = ?", $name); + }else{ + if(!$value) $value = ''; + $column_list[] = sprintf("[%s] = %s", $name, $value); + } - $column_list[] = sprintf("[%s] = %s", $name, $value); + } } @@ -536,8 +659,7 @@ } $condition = $this->getCondition($output); - - + if($output->list_count && $output->page) return $this->_getNavigationData($table_list, $columns, $left_join, $condition, $output); // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 @@ -555,7 +677,15 @@ $query = sprintf("%s from %s %s %s", $columns, implode(',',$table_list),implode(' ',$left_join), $condition); - if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups)); + if(count($output->groups)){ + foreach($output->groups as $k => $v ){ + if(preg_match('/^substr\(/i',$v)) $output->groups[$k] = preg_replace('/^substr\(/i','substring(',$v); + } + $query .= sprintf(' group by %s', implode(',',$output->groups)); + } + + + if($output->order && !preg_match('/count\(\*\)/i',$columns) ) { foreach($output->order as $key => $val) { @@ -568,10 +698,10 @@ // list_count를 사용할 경우 적용 if($output->list_count['value']) $query = sprintf('select top %d %s', $output->list_count['value'], $query); else $query = "select ".$query; - - $this->_query($query); + + $result = $this->_query($query); if($this->isError()) return; - $data = $this->_fetch(); + $data = $this->_fetch($result); $buff = new Object(); $buff->data = $data; @@ -587,14 +717,28 @@ require_once(_XE_PATH_.'classes/page/PageHandler.class.php'); // 전체 개수를 구함 - $count_condition = count($output->groups) ? sprintf('%s group by %s', $condition, implode(', ', $output->groups)) : $condition; + if(count($output->groups)){ + foreach($output->groups as $k => $v ){ + if(preg_match('/^substr\(/i',$v)) $output->groups[$k] = preg_replace('/^substr\(/i','substring(',$v); + } + $count_condition = sprintf('%s group by %s', $condition, implode(', ', $output->groups)); + }else{ + $count_condition = $condition; + } + + $total_count = $this->getCountCache($output->tables, $count_condition); if($total_count === false) { $count_query = sprintf("select count(*) as count from %s %s %s", implode(', ', $table_list), implode(' ', $left_join), $count_condition); if (count($output->groups)) $count_query = sprintf('select count(*) as count from (%s) xet', $count_query); - $this->_query($count_query); - $count_output = $this->_fetch(); - $total_count = (int)$count_output->count; + + $param = $this->param; + $result = $this->_query($count_query); + + $this->param = $param; + $count_output = $this->_fetch($result); + + $total_count = (int)$count_output->count; $this->putCountCache($output->tables, $count_condition, $total_count); } @@ -628,8 +772,13 @@ // group by 절 추가 - if(count($output->groups)) $group .= sprintf('group by %s', implode(',',$output->groups)); - + if(count($output->groups)){ + foreach($output->groups as $k => $v ){ + if(preg_match('/^substr\(/i',$v)) $output->groups[$k] = preg_replace('/^substr\(/i','substring(',$v); + } + $group .= sprintf('group by %s', implode(',',$output->groups)); + } + // order 절 추가 $order_targets = array(); if($output->order) { @@ -655,9 +804,12 @@ } // 1차로 order 대상에 해당 하는 값을 가져옴 + $param = $this->param; $first_query = sprintf("select %s from (select top %d %s from %s %s %s %s %s) xet", implode(',',$first_columns), $start_count, implode(',',$first_sub_columns), implode(',',$table_list), implode(' ',$left_join), $condition, $group, $order); - $this->_query($first_query); - $tmp = $this->_fetch(); + $result = $this->_query($first_query); + $this->param = $param; + $tmp = $this->_fetch($result); + // 1차에서 나온 값을 이용 다시 쿼리 실행 @@ -672,7 +824,8 @@ $query = sprintf('select top %d %s from %s %s %s %s %s', $list_count, $columns, implode(',',$table_list), implode(' ',$left_join), $condition, $group, $order); } - $this->_query($query); + $result = $this->_query($query); + if($this->isError()) { $buff = new Object(); $buff->total_count = 0; @@ -684,15 +837,14 @@ return $buff; } - $virtual_no = $total_count - ($page-1)*$list_count; - for($i=0;!$this->rs->EOF;$this->rs->MoveNext(),$i++){ - unset($row); - for($j=0;$j<$this->rs->Fields->Count;$j++){ - $row->{$this->rs[$j]->name} = $this->rs[$j]->value; - } - $data[$virtual_no--] = $row; - } - $this->rs = null; + $virtual_no = $total_count - ($page-1)*$list_count; + + $output = $this->_fetch($result); + if(!is_array($output)) $output = array($output); + + foreach($output as $k => $v) { + $data[$virtual_no--] = $v; + } $buff = new Object(); $buff->total_count = $total_count;