776 </div>'; |
782 </div>'; |
777 $template->footer(); |
783 $template->footer(); |
778 } |
784 } |
779 } |
785 } |
780 |
786 |
|
787 class postgresql { |
|
788 var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug; |
|
789 var $row = array(); |
|
790 var $rowset = array(); |
|
791 var $errhandler; |
|
792 |
|
793 function enable_errorhandler() |
|
794 { |
|
795 // echo "DBAL: enabling error handler<br />"; |
|
796 if ( function_exists('debug_backtrace') ) |
|
797 { |
|
798 $this->errhandler = set_error_handler('db_error_handler'); |
|
799 } |
|
800 } |
|
801 |
|
802 function disable_errorhandler() |
|
803 { |
|
804 // echo "DBAL: disabling error handler<br />"; |
|
805 if ( $this->errhandler ) |
|
806 { |
|
807 set_error_handler($this->errhandler); |
|
808 } |
|
809 else |
|
810 { |
|
811 restore_error_handler(); |
|
812 } |
|
813 } |
|
814 |
|
815 function sql_backtrace() |
|
816 { |
|
817 return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace); |
|
818 } |
|
819 |
|
820 function ensure_connection() |
|
821 { |
|
822 if(!$this->_conn) |
|
823 { |
|
824 $this->connect(); |
|
825 } |
|
826 } |
|
827 |
|
828 function _die($t = '') { |
|
829 if(defined('ENANO_HEADERS_SENT')) { |
|
830 ob_clean(); |
|
831 } |
|
832 header('HTTP/1.1 500 Internal Server Error'); |
|
833 $bt = $this->latest_query; // $this->sql_backtrace(); |
|
834 $e = htmlspecialchars(pg_last_error()); |
|
835 if($e=='') $e='<none>'; |
|
836 $t = ( !empty($t) ) ? $t : '<No error description provided>'; |
|
837 global $email; |
|
838 $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at <' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '>' : ''; |
|
839 $internal_text = '<h3>The site was unable to finish serving your request.</h3> |
|
840 <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p> |
|
841 <p>Description or location of error: '.$t.'<br /> |
|
842 Error returned by PostgreSQL extension: ' . $e . '<br /> |
|
843 Most recent SQL query:</p> |
|
844 <pre>'.$bt.'</pre>'; |
|
845 if(defined('ENANO_CONFIG_FETCHED')) die_semicritical('Database error', $internal_text); |
|
846 else grinding_halt('Database error', $internal_text); |
|
847 exit; |
|
848 } |
|
849 |
|
850 function die_json() |
|
851 { |
|
852 $e = addslashes(htmlspecialchars(pg_last_error())); |
|
853 $q = addslashes($this->latest_query); |
|
854 $t = "{'mode':'error','error':'An error occurred during database query.\nQuery was:\n $q\n\nError returned by PostgreSQL: $e'}"; |
|
855 die($t); |
|
856 } |
|
857 |
|
858 function get_error($t = '') { |
|
859 header('HTTP/1.1 500 Internal Server Error'); |
|
860 $bt = $this->sql_backtrace(); |
|
861 $e = htmlspecialchars(pg_last_error()); |
|
862 if($e=='') $e='<none>'; |
|
863 global $email; |
|
864 $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at <' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '>' : ''; |
|
865 $internal_text = '<h3>The site was unable to finish serving your request.</h3> |
|
866 <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p> |
|
867 <p>Description or location of error: '.$t.'<br /> |
|
868 Error returned by MySQL extension: ' . $e . '<br /> |
|
869 Most recent SQL query:</p> |
|
870 <pre>'.$bt.'</pre>'; |
|
871 return $internal_text; |
|
872 } |
|
873 |
|
874 function connect() |
|
875 { |
|
876 $this->enable_errorhandler(); |
|
877 |
|
878 define('ENANO_DBLAYER', 'PGSQL'); |
|
879 define('ENANO_SQLFUNC_LOWERCASE', 'lower'); |
|
880 define('ENANO_SQL_MULTISTRING_PRFIX', 'E'); |
|
881 define('ENANO_SQL_BOOLEAN_TRUE', '1'); |
|
882 define('ENANO_SQL_BOOLEAN_FALSE', '0'); |
|
883 |
|
884 if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') ) |
|
885 { |
|
886 @include(ENANO_ROOT.'/config.new.php'); |
|
887 } |
|
888 else |
|
889 { |
|
890 @include(ENANO_ROOT.'/config.php'); |
|
891 } |
|
892 |
|
893 if ( isset($crypto_key) ) |
|
894 unset($crypto_key); // Get this sucker out of memory fast |
|
895 |
|
896 if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') ) |
|
897 { |
|
898 // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects |
|
899 if ( !defined('scriptPath') ) |
|
900 { |
|
901 if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) ) |
|
902 { |
|
903 $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']); |
|
904 } |
|
905 if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) ) |
|
906 { |
|
907 // user requested http://foo/enano as opposed to http://foo/enano/index.php |
|
908 $_SERVER['REQUEST_URI'] .= '/index.php'; |
|
909 } |
|
910 $sp = dirname($_SERVER['REQUEST_URI']); |
|
911 if($sp == '/' || $sp == '\\') $sp = ''; |
|
912 define('scriptPath', $sp); |
|
913 define('contentPath', "$sp/index.php?title="); |
|
914 } |
|
915 $loc = scriptPath . '/install.php'; |
|
916 // header("Location: $loc"); |
|
917 redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3); |
|
918 exit; |
|
919 } |
|
920 $this->_conn = @pg_connect("host=$dbhost port=5432 dbname=$dbname user=$dbuser password=$dbpasswd"); |
|
921 unset($dbuser); |
|
922 unset($dbpasswd); // Security |
|
923 |
|
924 if ( !$this->_conn ) |
|
925 { |
|
926 grinding_halt('Enano is having a problem', '<p>Error: couldn\'t connect to PostgreSQL.<br />'.pg_last_error().'</p>'); |
|
927 } |
|
928 |
|
929 // Reset some variables |
|
930 $this->query_backtrace = array(); |
|
931 $this->query_times = array(); |
|
932 $this->query_sources = array(); |
|
933 $this->num_queries = 0; |
|
934 |
|
935 $this->debug = ( defined('ENANO_DEBUG') ); |
|
936 |
|
937 // We're in! |
|
938 $this->disable_errorhandler(); |
|
939 return true; |
|
940 } |
|
941 |
|
942 function sql_query($q) |
|
943 { |
|
944 $this->enable_errorhandler(); |
|
945 |
|
946 if ( $this->debug && function_exists('debug_backtrace') ) |
|
947 { |
|
948 $backtrace = @debug_backtrace(); |
|
949 if ( is_array($backtrace) ) |
|
950 { |
|
951 $bt = $backtrace[0]; |
|
952 if ( isset($backtrace[1]['class']) ) |
|
953 { |
|
954 if ( $backtrace[1]['class'] == 'sessionManager' ) |
|
955 { |
|
956 $bt = $backtrace[1]; |
|
957 } |
|
958 } |
|
959 $this->query_sources[$q] = substr($bt['file'], strlen(ENANO_ROOT) + 1) . ', line ' . $bt['line']; |
|
960 } |
|
961 unset($backtrace); |
|
962 } |
|
963 |
|
964 $this->num_queries++; |
|
965 $this->query_backtrace[] = $q; |
|
966 $this->latest_query = $q; |
|
967 // First make sure we have a connection |
|
968 if ( !$this->_conn ) |
|
969 { |
|
970 $this->_die('A database connection has not yet been established.'); |
|
971 } |
|
972 // Does this query look malicious? |
|
973 if ( !$this->check_query($q) ) |
|
974 { |
|
975 $this->report_query($q); |
|
976 grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>'); |
|
977 } |
|
978 |
|
979 $time_start = microtime_float(); |
|
980 $r = pg_query($q); |
|
981 $this->query_times[$q] = microtime_float() - $time_start; |
|
982 $this->latest_result = $r; |
|
983 $this->disable_errorhandler(); |
|
984 return $r; |
|
985 } |
|
986 |
|
987 function sql_unbuffered_query($q) |
|
988 { |
|
989 $this->enable_errorhandler(); |
|
990 |
|
991 $this->num_queries++; |
|
992 $this->query_backtrace[] = '(UNBUFFERED) ' . $q; |
|
993 $this->latest_query = $q; |
|
994 // First make sure we have a connection |
|
995 if ( !$this->_conn ) |
|
996 { |
|
997 $this->_die('A database connection has not yet been established.'); |
|
998 } |
|
999 // Does this query look malicious? |
|
1000 if ( !$this->check_query($q) ) |
|
1001 { |
|
1002 $this->report_query($q); |
|
1003 grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>'); |
|
1004 } |
|
1005 |
|
1006 $time_start = microtime_float(); |
|
1007 $r = pg_query($q); |
|
1008 $this->query_times[$q] = microtime_float() - $time_start; |
|
1009 $this->latest_result = $r; |
|
1010 $this->disable_errorhandler(); |
|
1011 return $r; |
|
1012 } |
|
1013 |
|
1014 /** |
|
1015 * Checks a SQL query for possible signs of injection attempts |
|
1016 * @param string $q the query to check |
|
1017 * @return bool true if query passed check, otherwise false |
|
1018 */ |
|
1019 |
|
1020 function check_query($q, $debug = false) |
|
1021 { |
|
1022 if($debug) echo "\$db->check_query(): checking query: ".htmlspecialchars($q).'<br />'."\n"; |
|
1023 $sz = strlen($q); |
|
1024 $quotechar = false; |
|
1025 $quotepos = 0; |
|
1026 $prev_is_quote = false; |
|
1027 $just_started = false; |
|
1028 for ( $i = 0; $i < strlen($q); $i++, $c = substr($q, $i, 1) ) |
|
1029 { |
|
1030 $next = substr($q, $i+1, 1); |
|
1031 $next2 = substr($q, $i+2, 1); |
|
1032 $prev = substr($q, $i-1, 1); |
|
1033 $prev2 = substr($q, $i-2, 1); |
|
1034 if(isset($c) && in_array($c, Array('"', "'", '`'))) |
|
1035 { |
|
1036 if($quotechar) |
|
1037 { |
|
1038 if ( |
|
1039 ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_started ) && $prev != '\\') || |
|
1040 ( $prev2 == '\\' && $prev == $quotechar && $quotechar == $c ) |
|
1041 ) |
|
1042 { |
|
1043 $quotechar = false; |
|
1044 if($debug) echo('$db->check_query(): just finishing a quote section, quoted string: '.htmlspecialchars(substr($q, $quotepos, $i - $quotepos + 1)) . '<br />'); |
|
1045 $q = substr($q, 0, $quotepos) . 'SAFE_QUOTE' . substr($q, $i + 1, strlen($q)); |
|
1046 if($debug) echo('$db->check_query(): Filtered query: '.$q.'<br />'); |
|
1047 $i = $quotepos; |
|
1048 } |
|
1049 } |
|
1050 else |
|
1051 { |
|
1052 $quotechar = $c; |
|
1053 $quotepos = $i; |
|
1054 $just_started = true; |
|
1055 } |
|
1056 if($debug) echo '$db->check_query(): found quote char as pos: '.$i.'<br />'; |
|
1057 continue; |
|
1058 } |
|
1059 $just_started = false; |
|
1060 } |
|
1061 if(substr(trim($q), strlen(trim($q))-1, 1) == ';') $q = substr(trim($q), 0, strlen(trim($q))-1); |
|
1062 for($i=0;$i<strlen($q);$i++,$c=substr($q, $i, 1)) |
|
1063 { |
|
1064 if ( |
|
1065 ( ( $c == ';' && $i != $sz-1 ) || $c . substr($q, $i+1, 1) == '--' ) |
|
1066 || ( in_array($c, Array('"', "'", '`')) ) |
|
1067 ) // Don't permit semicolons in mid-query, and never allow comments |
|
1068 { |
|
1069 // Injection attempt! |
|
1070 if($debug) |
|
1071 { |
|
1072 $e = ''; |
|
1073 for($j=$i-5;$j<$i+5;$j++) |
|
1074 { |
|
1075 if($j == $i) $e .= '<span style="color: red; text-decoration: underline;">' . $c . '</span>'; |
|
1076 else $e .= $c; |
|
1077 } |
|
1078 echo 'Injection attempt caught at pos: '.$i.'<br />'; |
|
1079 } |
|
1080 return false; |
|
1081 } |
|
1082 } |
|
1083 if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) ) |
|
1084 { |
|
1085 if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>'; |
|
1086 return false; |
|
1087 } |
|
1088 return true; |
|
1089 } |
|
1090 |
|
1091 /** |
|
1092 * Set the internal result pointer to X |
|
1093 * @param int $pos The number of the row |
|
1094 * @param resource $result The MySQL result resource - if not given, the latest cached query is assumed |
|
1095 * @return true on success, false on failure |
|
1096 */ |
|
1097 |
|
1098 function sql_data_seek($pos, $result = false) |
|
1099 { |
|
1100 $this->enable_errorhandler(); |
|
1101 if(!$result) |
|
1102 $result = $this->latest_result; |
|
1103 if(!$result) |
|
1104 { |
|
1105 $this->disable_errorhandler(); |
|
1106 return false; |
|
1107 } |
|
1108 if(pg_result_seek($result, $pos)) |
|
1109 { |
|
1110 $this->disable_errorhandler(); |
|
1111 return true; |
|
1112 } |
|
1113 else |
|
1114 { |
|
1115 $this->disable_errorhandler(); |
|
1116 return false; |
|
1117 } |
|
1118 } |
|
1119 |
|
1120 /** |
|
1121 * Reports a bad query to the admin |
|
1122 * @param string $query the naughty query |
|
1123 * @access private |
|
1124 */ |
|
1125 |
|
1126 function report_query($query) |
|
1127 { |
|
1128 global $session; |
|
1129 if(is_object($session) && defined('ENANO_MAINSTREAM')) |
|
1130 $username = $session->username; |
|
1131 else |
|
1132 $username = 'Unavailable'; |
|
1133 $query = $this->escape($query); |
|
1134 $q = $this->sql_query('INSERT INTO '.table_prefix.'logs(log_type, action, time_id, date_string, page_text, author, edit_summary) |
|
1135 VALUES(\'security\', \'sql_inject\', '.time().', \'\', \''.$query.'\', \''.$username.'\', \''.$_SERVER['REMOTE_ADDR'].'\');'); |
|
1136 } |
|
1137 |
|
1138 /** |
|
1139 * Returns the ID of the row last inserted. |
|
1140 * @return int |
|
1141 */ |
|
1142 |
|
1143 function insert_id() |
|
1144 { |
|
1145 return @pg_last_oid(); |
|
1146 } |
|
1147 |
|
1148 function fetchrow($r = false) { |
|
1149 $this->enable_errorhandler(); |
|
1150 if(!$this->_conn) return false; |
|
1151 if(!$r) $r = $this->latest_result; |
|
1152 if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); |
|
1153 $row = pg_fetch_assoc($r); |
|
1154 $this->disable_errorhandler(); |
|
1155 return $row; |
|
1156 } |
|
1157 |
|
1158 function fetchrow_num($r = false) { |
|
1159 $this->enable_errorhandler(); |
|
1160 if(!$r) $r = $this->latest_result; |
|
1161 if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); |
|
1162 $row = pg_fetch_row($r); |
|
1163 $this->disable_errorhandler(); |
|
1164 return $row; |
|
1165 } |
|
1166 |
|
1167 function numrows($r = false) { |
|
1168 $this->enable_errorhandler(); |
|
1169 if(!$r) $r = $this->latest_result; |
|
1170 if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); |
|
1171 $n = pg_num_rows($r); |
|
1172 $this->disable_errorhandler(); |
|
1173 return $n; |
|
1174 } |
|
1175 |
|
1176 function escape($str) |
|
1177 { |
|
1178 $this->enable_errorhandler(); |
|
1179 $str = pg_escape_string($str); |
|
1180 $this->disable_errorhandler(); |
|
1181 return $str; |
|
1182 } |
|
1183 |
|
1184 function free_result($result = false) |
|
1185 { |
|
1186 $this->enable_errorhandler(); |
|
1187 if(!$result) |
|
1188 $result = $this->latest_result; |
|
1189 if(!$result) |
|
1190 { |
|
1191 $this->disable_errorhandler(); |
|
1192 return null; |
|
1193 } |
|
1194 pg_free_result($result); |
|
1195 $this->disable_errorhandler(); |
|
1196 return null; |
|
1197 } |
|
1198 |
|
1199 function close() { |
|
1200 pg_close($this->_conn); |
|
1201 unset($this->_conn); |
|
1202 } |
|
1203 |
|
1204 // phpBB DBAL compatibility |
|
1205 function sql_fetchrow($r = false) |
|
1206 { |
|
1207 return $this->fetchrow($r); |
|
1208 } |
|
1209 function sql_freeresult($r = false) |
|
1210 { |
|
1211 if(!$this->_conn) return false; |
|
1212 if(!$r) $r = $this->latest_result; |
|
1213 if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); |
|
1214 $this->free_result($r); |
|
1215 } |
|
1216 function sql_numrows($r = false) |
|
1217 { |
|
1218 return $this->numrows(); |
|
1219 } |
|
1220 function sql_affectedrows($r = false, $f, $n) |
|
1221 { |
|
1222 if(!$this->_conn) return false; |
|
1223 if(!$r) $r = $this->latest_result; |
|
1224 if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); |
|
1225 return pg_affected_rows(); |
|
1226 } |
|
1227 |
|
1228 function sql_type_cast(&$value) |
|
1229 { |
|
1230 if ( is_float($value) ) |
|
1231 { |
|
1232 return doubleval($value); |
|
1233 } |
|
1234 if ( is_integer($value) || is_bool($value) ) |
|
1235 { |
|
1236 return intval($value); |
|
1237 } |
|
1238 if ( is_string($value) || empty($value) ) |
|
1239 { |
|
1240 return '\'' . $this->sql_escape_string($value) . '\''; |
|
1241 } |
|
1242 // uncastable var : let's do a basic protection on it to prevent sql injection attempt |
|
1243 return '\'' . $this->sql_escape_string(htmlspecialchars($value)) . '\''; |
|
1244 } |
|
1245 |
|
1246 function sql_statement(&$fields, $fields_inc='') |
|
1247 { |
|
1248 // init result |
|
1249 $this->sql_fields = $this->sql_values = $this->sql_update = ''; |
|
1250 if ( empty($fields) && empty($fields_inc) ) |
|
1251 { |
|
1252 return; |
|
1253 } |
|
1254 |
|
1255 // process |
|
1256 if ( !empty($fields) ) |
|
1257 { |
|
1258 $first = true; |
|
1259 foreach ( $fields as $field => $value ) |
|
1260 { |
|
1261 // field must contain a field name |
|
1262 if ( !empty($field) && is_string($field) ) |
|
1263 { |
|
1264 $value = $this->sql_type_cast($value); |
|
1265 $this->sql_fields .= ( $first ? '' : ', ' ) . $field; |
|
1266 $this->sql_values .= ( $first ? '' : ', ' ) . $value; |
|
1267 $this->sql_update .= ( $first ? '' : ', ' ) . $field . ' = ' . $value; |
|
1268 $first = false; |
|
1269 } |
|
1270 } |
|
1271 } |
|
1272 if ( !empty($fields_inc) ) |
|
1273 { |
|
1274 foreach ( $fields_inc as $field => $indent ) |
|
1275 { |
|
1276 if ( $indent != 0 ) |
|
1277 { |
|
1278 $this->sql_update .= (empty($this->sql_update) ? '' : ', ') . $field . ' = ' . $field . ($indent < 0 ? ' - ' : ' + ') . abs($indent); |
|
1279 } |
|
1280 } |
|
1281 } |
|
1282 } |
|
1283 |
|
1284 function sql_stack_reset($id='') |
|
1285 { |
|
1286 if ( empty($id) ) |
|
1287 { |
|
1288 $this->sql_stack_fields = array(); |
|
1289 $this->sql_stack_values = array(); |
|
1290 } |
|
1291 else |
|
1292 { |
|
1293 $this->sql_stack_fields[$id] = array(); |
|
1294 $this->sql_stack_values[$id] = array(); |
|
1295 } |
|
1296 } |
|
1297 |
|
1298 function sql_stack_statement(&$fields, $id='') |
|
1299 { |
|
1300 $this->sql_statement($fields); |
|
1301 if ( empty($id) ) |
|
1302 { |
|
1303 $this->sql_stack_fields = $this->sql_fields; |
|
1304 $this->sql_stack_values[] = '(' . $this->sql_values . ')'; |
|
1305 } |
|
1306 else |
|
1307 { |
|
1308 $this->sql_stack_fields[$id] = $this->sql_fields; |
|
1309 $this->sql_stack_values[$id][] = '(' . $this->sql_values . ')'; |
|
1310 } |
|
1311 } |
|
1312 |
|
1313 function sql_stack_insert($table, $transaction=false, $line='', $file='', $break_on_error=true, $id='') |
|
1314 { |
|
1315 if ( (empty($id) && empty($this->sql_stack_values)) || (!empty($id) && empty($this->sql_stack_values[$id])) ) |
|
1316 { |
|
1317 return false; |
|
1318 } |
|
1319 switch( SQL_LAYER ) |
|
1320 { |
|
1321 case 'mysql': |
|
1322 case 'mysql4': |
|
1323 if ( empty($id) ) |
|
1324 { |
|
1325 $sql = 'INSERT INTO ' . $table . ' |
|
1326 (' . $this->sql_stack_fields . ') VALUES ' . implode(",\n", $this->sql_stack_values); |
|
1327 } |
|
1328 else |
|
1329 { |
|
1330 $sql = 'INSERT INTO ' . $table . ' |
|
1331 (' . $this->sql_stack_fields[$id] . ') VALUES ' . implode(",\n", $this->sql_stack_values[$id]); |
|
1332 } |
|
1333 $this->sql_stack_reset($id); |
|
1334 return $this->sql_query($sql, $transaction, $line, $file, $break_on_error); |
|
1335 break; |
|
1336 default: |
|
1337 $count_sql_stack_values = empty($id) ? count($this->sql_stack_values) : count($this->sql_stack_values[$id]); |
|
1338 $result = !empty($count_sql_stack_values); |
|
1339 for ( $i = 0; $i < $count_sql_stack_values; $i++ ) |
|
1340 { |
|
1341 if ( empty($id) ) |
|
1342 { |
|
1343 $sql = 'INSERT INTO ' . $table . ' |
|
1344 (' . $this->sql_stack_fields . ') VALUES ' . $this->sql_stack_values[$i]; |
|
1345 } |
|
1346 else |
|
1347 { |
|
1348 $sql = 'INSERT INTO ' . $table . ' |
|
1349 (' . $this->sql_stack_fields[$id] . ') VALUES ' . $this->sql_stack_values[$id][$i]; |
|
1350 } |
|
1351 $result &= $this->sql_query($sql, $transaction, $line, $file, $break_on_error); |
|
1352 } |
|
1353 $this->sql_stack_reset($id); |
|
1354 return $result; |
|
1355 break; |
|
1356 } |
|
1357 } |
|
1358 |
|
1359 function sql_subquery($field, $sql, $line='', $file='', $break_on_error=true, $type=TYPE_INT) |
|
1360 { |
|
1361 // sub-queries doable |
|
1362 $this->sql_get_version(); |
|
1363 if ( !in_array(SQL_LAYER, array('mysql', 'mysql4')) || (($this->sql_version[0] + ($this->sql_version[1] / 100)) >= 4.01) ) |
|
1364 { |
|
1365 return $sql; |
|
1366 } |
|
1367 |
|
1368 // no sub-queries |
|
1369 $ids = array(); |
|
1370 $result = $this->sql_query(trim($sql), false, $line, $file, $break_on_error); |
|
1371 while ( $row = $this->sql_fetchrow($result) ) |
|
1372 { |
|
1373 $ids[] = $type == TYPE_INT ? intval($row[$field]) : '\'' . $this->sql_escape_string($row[$field]) . '\''; |
|
1374 } |
|
1375 $this->sql_freeresult($result); |
|
1376 return empty($ids) ? 'NULL' : implode(', ', $ids); |
|
1377 } |
|
1378 |
|
1379 function sql_col_id($expr, $alias) |
|
1380 { |
|
1381 $this->sql_get_version(); |
|
1382 return in_array(SQL_LAYER, array('mysql', 'mysql4')) && (($this->sql_version[0] + ($this->sql_version[1] / 100)) <= 4.01) ? $alias : $expr; |
|
1383 } |
|
1384 |
|
1385 function sql_get_version() |
|
1386 { |
|
1387 if ( empty($this->sql_version) ) |
|
1388 { |
|
1389 $this->sql_version = array(0, 0, 0); |
|
1390 switch ( SQL_LAYER ) |
|
1391 { |
|
1392 case 'mysql': |
|
1393 case 'mysql4': |
|
1394 if ( function_exists('mysql_get_server_info') ) |
|
1395 { |
|
1396 $lo_version = explode('-', mysql_get_server_info()); |
|
1397 $this->sql_version = explode('.', $lo_version[0]); |
|
1398 $this->sql_version = array(intval($this->sql_version[0]), intval($this->sql_version[1]), intval($this->sql_version[2]), $lo_version[1]); |
|
1399 } |
|
1400 break; |
|
1401 |
|
1402 case 'postgresql': |
|
1403 case 'mssql': |
|
1404 case 'mssql-odbc': |
|
1405 default: |
|
1406 break; |
|
1407 } |
|
1408 } |
|
1409 return $this->sql_version; |
|
1410 } |
|
1411 |
|
1412 function sql_error() |
|
1413 { |
|
1414 if ( $this->_conn ) |
|
1415 { |
|
1416 return mysql_error(); |
|
1417 } |
|
1418 else |
|
1419 { |
|
1420 return array(); |
|
1421 } |
|
1422 } |
|
1423 function sql_escape_string($t) |
|
1424 { |
|
1425 return mysql_real_escape_string($t); |
|
1426 } |
|
1427 function sql_close() |
|
1428 { |
|
1429 $this->close(); |
|
1430 } |
|
1431 function sql_fetchrowset($query_id = 0) |
|
1432 { |
|
1433 if( !$query_id ) |
|
1434 { |
|
1435 $query_id = $this->query_result; |
|
1436 } |
|
1437 |
|
1438 if( $query_id ) |
|
1439 { |
|
1440 unset($this->rowset[$query_id]); |
|
1441 unset($this->row[$query_id]); |
|
1442 |
|
1443 while($this->rowset[$query_id] = mysql_fetch_array($query_id, MYSQL_ASSOC)) |
|
1444 { |
|
1445 $result[] = $this->rowset[$query_id]; |
|
1446 } |
|
1447 |
|
1448 return $result; |
|
1449 } |
|
1450 else |
|
1451 { |
|
1452 return false; |
|
1453 } |
|
1454 } |
|
1455 /** |
|
1456 * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with. |
|
1457 */ |
|
1458 |
|
1459 function sql_report() |
|
1460 { |
|
1461 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1462 if ( !$session->get_permissions('mod_misc') ) |
|
1463 { |
|
1464 die_friendly('Access denied', '<p>You are not authorized to generate a SQL backtrace.</p>'); |
|
1465 } |
|
1466 // Create copies of variables that may be changed after header is called |
|
1467 $backtrace = $this->query_backtrace; |
|
1468 $times = $this->query_times; |
|
1469 $template->header(); |
|
1470 echo '<h3>SQL query log and timetable</h3>'; |
|
1471 echo '<div class="tblholder"> |
|
1472 <table border="0" cellspacing="1" cellpadding="4">'; |
|
1473 $i = 0; |
|
1474 foreach ( $backtrace as $query ) |
|
1475 { |
|
1476 $i++; |
|
1477 $unbuffered = false; |
|
1478 if ( substr($query, 0, 13) == '(UNBUFFERED) ' ) |
|
1479 { |
|
1480 $query = substr($query, 13); |
|
1481 $unbuffered = true; |
|
1482 } |
|
1483 if ( $i == 1 ) |
|
1484 { |
|
1485 echo '<tr> |
|
1486 <th colspan="2">SQL backtrace for a normal page load of ' . htmlspecialchars($paths->cpage['urlname']) . '</th> |
|
1487 </tr>'; |
|
1488 } |
|
1489 else |
|
1490 { |
|
1491 echo '<tr> |
|
1492 <th class="subhead" colspan="2"> </th> |
|
1493 </tr>'; |
|
1494 } |
|
1495 echo '<tr> |
|
1496 <td class="row2">Query:</td> |
|
1497 <td class="row1"><pre>' . htmlspecialchars($query) . '</pre></td> |
|
1498 </tr> |
|
1499 <tr> |
|
1500 <td class="row2">Time:</td> |
|
1501 <td class="row1">' . number_format($this->query_times[$query], 6) . ' seconds</td> |
|
1502 </tr> |
|
1503 <tr> |
|
1504 <td class="row2">Unbuffered:</td> |
|
1505 <td class="row1">' . ( $unbuffered ? 'Yes' : 'No' ) . '</td> |
|
1506 </tr>'; |
|
1507 if ( isset($this->query_sources[$query]) ) |
|
1508 { |
|
1509 echo '<tr> |
|
1510 <td class="row2">Called from:</td> |
|
1511 <td class="row1">' . $this->query_sources[$query] . '</td> |
|
1512 </tr>'; |
|
1513 } |
|
1514 } |
|
1515 if ( function_exists('array_sum') ) |
|
1516 { |
|
1517 $query_time_total = array_sum($this->query_times); |
|
1518 echo '<tr> |
|
1519 <th class="subhead" colspan="2"> |
|
1520 Total time taken for SQL queries: ' . round( $query_time_total, 6 ) . ' seconds |
|
1521 </th> |
|
1522 </tr>'; |
|
1523 } |
|
1524 echo ' </table> |
|
1525 </div>'; |
|
1526 $template->footer(); |
|
1527 } |
|
1528 } |
|
1529 |
781 ?> |
1530 ?> |