or = character before empty string ''. * * @since 5.2 * * @param string $sql SQL queries. * @return string Filtered SQL queries. */ function db_sql_filter( $sql ) { // Replace empty strings ('') with NULL values. if ( stripos( $sql, 'INSERT INTO ' ) !== false ) { // Check for ( or , character before empty string ''. $sql = preg_replace( "/([,\(])[\r\n\t ]*''(?!')/", '\\1NULL', $sql ); } // Check for <> or = character before empty string ''. $sql = preg_replace( "/(<>|=)[\r\n\t ]*''(?!'|\w|\d)/", '\\1NULL', $sql ); /** * IS NOT NULL cases * * Replace <>NULL & !=NULL with IS NOT NULL * * @link http://www.postgresql.org/docs/current/static/functions-comparison.html */ $sql = str_ireplace( [ '<>NULL', '!=NULL' ], ' IS NOT NULL', $sql ); return $sql; } /** * This function connects, and does the passed query, then returns a result resource * Not receiving the return == unusable search. * * @example $processable_results = DBQuery( "SELECT * FROM students" ); * * @uses db_sql_filter() * @uses db_query() * @see DBGet() * * @since 4.3 Do DBQuery after action hook. * * @param string $sql SQL statement. * @return resource PostgreSQL result resource */ function DBQuery( $sql ) { $sql = db_sql_filter( $sql ); $result = db_query( $sql ); // Do DBQuery after action hook. do_action( 'database.inc.php|dbquery_after', [ $sql, $result ] ); return $result; } /** * Return next row * * @since 10.0 Add MySQL support * @since 10.2.1 Fix error mysqli_fetch_assoc(): Argument #1 must be of type mysqli_result, null given * * @global $DatabaseType Database type: mysql or postgresql * * @param resource PostgreSQL result resource $result Result. * @return array|bool Next row in result set or false. */ function db_fetch_row( $result ) { global $DatabaseType; $return = false; if ( $DatabaseType === 'mysql' && $result instanceof mysqli_result ) { $return = mysqli_fetch_assoc( $result ); } else { $return = pg_fetch_array( $result, null, PGSQL_ASSOC ); } return is_array( $return ) ? array_change_key_case( $return, CASE_UPPER ) : $return; } /** * Returns code to go into SQL statement for accessing the next value of a sequence * * @deprecated since 9.2.1 Use DBLastInsertID() instead * * @global $DatabaseType Database type: mysql or postgresql * * @param string $seqname PostgreSQL sequence name. * @return sting nextval code */ function db_seq_nextval( $seqname ) { global $DatabaseType; if ( $DatabaseType === 'mysql' ) { return DBSeqNextID( $seqname ); } return "nextval('" . DBEscapeString( $seqname ) . "')"; } /** * DB Sequence Next ID * * @deprecated since 9.2.1 Use DBLastInsertID() instead (with the exception of student ID) * * @since 11.0.1 MySQL fix infinite loop, emulate PostgreSQL's nextval() * @since 11.2.4 MySQL 8+ fix infinite loop due to cached AUTO_INCREMENT * * @example $id = DBSeqNextID( 'people_person_id_seq' ); * * @global $DatabaseType Database type: mysql or postgresql * * @param string $seqname Sequence name (or table name for MySQL). * * @return int Next ID. */ function DBSeqNextID( $seqname ) { global $DatabaseType; static $auto_increment = []; if ( $DatabaseType === 'mysql' ) { if ( empty( $auto_increment ) // Set only once per session. && DBGetOne( "SHOW VARIABLES LIKE 'information_schema_stats_expiry';" ) ) { /** * Do NOT cache table statistics cache in MySQL 8+ * * Note: only for MySQL, MariaDB does not have this system variable * * @link https://stackoverflow.com/questions/51283195/wrong-auto-increment-value-on-select * * @since 11.2.4 MySQL 8+ fix infinite loop due to cached AUTO_INCREMENT */ DBQuery( "SET @@SESSION.information_schema_stats_expiry = 0;" ); } // Try to get table name from PostgreSQL sequence name by removing '_id_seq'. // Will fail if PRIMARY KEY / serial column name is != id. $table_name = str_ireplace( [ '_id_seq', '_seq' ], '', $seqname ); $seq_next_RET = db_fetch_row( DBQuery( "SELECT AUTO_INCREMENT FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name='" . mb_strtolower( DBEscapeString( $table_name ) ) . "'" ) ); // Return 0 if query failed. 0 in a MySQL query is valid for an AUTO_INCREMENT ID column. $seq_next_id = empty( $seq_next_RET ) ? 0 : $seq_next_RET['AUTO_INCREMENT']; if ( $seq_next_id ) { if ( empty( $auto_increment[ $table_name ] ) ) { $auto_increment[ $table_name ] = $seq_next_id; } elseif ( $auto_increment[ $table_name ] == $seq_next_id ) { /** * Manually increment AUTO_INCREMENT * * @since 11.0.1 MySQL fix infinite loop, emulate PostgreSQL's nextval() */ $seq_next_id++; DBQuery( "ALTER TABLE " . DBEscapeIdentifier( $table_name ) . " AUTO_INCREMENT=" . (int) $seq_next_id ); $auto_increment[ $table_name ] = $seq_next_id; } else { unset( $auto_increment[ $table_name ] ); } } } else { $seq_next_RET = db_fetch_row( DBQuery( "SELECT " . db_seq_nextval( $seqname ) . ' AS ID' ) ); $seq_next_id = $seq_next_RET['ID']; } return $seq_next_id; } /** * DB Last Inserted ID * * @since 9.2.1 * @since 10.0 Add MySQL support * * @link https://stackoverflow.com/questions/2944297/postgresql-function-for-last-inserted-id * * @return int Last ID. */ function DBLastInsertID() { global $DatabaseType; $last_insert_id_function = $DatabaseType === 'mysql' ? 'LAST_INSERT_ID()' : 'LASTVAL()'; $last_insert_id_RET = db_fetch_row( DBQuery( "SELECT " . $last_insert_id_function . ' AS ID' ) ); return $last_insert_id_RET['ID']; } /** * Start transaction * * @return void */ function db_trans_start() { db_query( 'BEGIN;' ); } /** * Run query on transaction -- if failure, runs rollback * * @since 5.2 $connection param removed. * * @param string $sql SQL statement. * @return PostgreSQL result resource */ function db_trans_query( $sql, $show_error = true ) { $sql = db_sql_filter( $sql ); // Use @ error control operator to silence PHP Warning in case of failure. $result = @db_query( $sql, $show_error ); if ( $result === false ) { // Rollback commands. db_trans_rollback(); } return $result; } /** * Commit changes * * @param PostgreSQL connection resource $connection Connection. DEPRECATED. * @return void */ function db_trans_commit() { db_query( 'COMMIT;' ); } /** * Rollback changes * * @since 5.2 * * @return void */ function db_trans_rollback() { db_query( 'ROLLBACK;' ); } /** * Dry run query on transaction -- rollback anyway * Useful to check first if foreign key constraints are preventing DELETE. * * @since 5.2 * * @example $can_delete = DBTransDryRun( UserDeleteSQL( UserStaffID() ) ); * * @param string $sql SQL statement. * @return bool Can run the queries in the transaction without error? */ function DBTransDryRun( $sql ) { db_trans_start(); $result = db_trans_query( $sql, false ); if ( $result !== false ) { // Rollback transaction anyway. db_trans_rollback(); } return $result !== false; } /** * Generate CASE-WHEN condition * * @example db_case( [ 'FAILED_LOGIN', "''", '1', 'FAILED_LOGIN+1' ] ) * will return ' CASE WHEN FAILED_LOGIN IS NULL THEN 1 ELSE FAILED_LOGIN+1 END ' * * @param array $array [ Column, IS, THEN, ELSE ] * @return string CASE-WHEN condition */ function db_case( $array ) { $counter = 0; $array_count = count( $array ); $string = ' CASE WHEN ' . $array[0] . ' ='; $counter++; $arr_count = count( $array ); for ( $i = 1; $i < $arr_count; $i++ ) { $value = $array[$i]; if ( $value == "''" && mb_substr( $string, -1 ) == '=' ) { $value = ' IS NULL'; $string = mb_substr( $string, 0, -1 ); } $string .= $value; if ( $counter == ( $array_count - 2 ) && $array_count % 2 == 0 ) { $string .= ' ELSE '; } elseif ( $counter == ( $array_count - 1 ) ) { $string .= ' END '; } elseif ( $counter % 2 == 0 ) { $string .= ' WHEN ' . $array[0] . '='; } elseif ( $counter % 2 == 1 ) { $string .= ' THEN '; } $counter++; } return $string; } /** * Returns an array with the field names for the specified table as key with subkeys * of SIZE, TYPE, SCALE and NULL. TYPE: varchar, numeric, etc. * * @since 10.0 Add MySQL support * * @global $DatabaseType Database type: mysql or postgresql * * @param string $table DB Table. * @return array Table properties */ function db_properties( $table ) { global $DatabaseType; if ( $DatabaseType === 'mysql' ) { $sql = "SHOW COLUMNS FROM " . DBEscapeIdentifier( $table ); } else { $sql = "SELECT a.attnum,a.attname AS field,t.typname AS type, a.attlen AS length,a.atttypmod AS lengthvar,a.attnotnull AS notnull FROM pg_class c,pg_attribute a,pg_type t WHERE c.relname='" . mb_strtolower( DBEscapeString( $table ) ) . "' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid ORDER BY a.attnum"; } $result = DBQuery( $sql ); while ( $row = db_fetch_row( $result ) ) { $field = mb_strtoupper( $row['FIELD'] ); if ( $DatabaseType === 'mysql' ) { $open_parens_pos = mb_strpos( $row['TYPE'], '(' ); $properties[$field]['TYPE'] = mb_strtoupper( mb_substr( $row['TYPE'], 0, $open_parens_pos ? $open_parens_pos : null ) ); if ( ! $pos = mb_strpos( $row['TYPE'], ',' ) ) { $pos = $open_parens_pos; } else { $properties[$field]['SCALE'] = mb_substr( $row['TYPE'], $pos + 1, -1 ); } $properties[$field]['SIZE'] = ''; if ( $open_parens_pos ) { $properties[$field]['SIZE'] = mb_substr( $row['TYPE'], $open_parens_pos + 1, ( $pos !== $open_parens_pos ? $pos - $open_parens_pos -1 : -1 ) ); } $properties[$field]['NULL'] = $row['NULL'] != '' && $row['NULL'] !== 'NO' ? 'Y' : 'N'; continue; } $properties[$field]['TYPE'] = mb_strtoupper( $row['TYPE'] ); if ( mb_strtoupper( $row['TYPE'] ) == 'NUMERIC' ) { $properties[$field]['SIZE'] = ( $row['LENGTHVAR'] >> 16 ) & 0xffff; $properties[$field]['SCALE'] = ( $row['LENGTHVAR'] - 4 ) & 0xffff; } else { if ( $row['LENGTH'] > 0 ) { $properties[$field]['SIZE'] = $row['LENGTH']; } elseif ( $row['LENGTHVAR'] > 0 ) { $properties[$field]['SIZE'] = $row['LENGTHVAR'] - 4; } } $properties[$field]['NULL'] = $row['NOTNULL'] === 't' ? 'N' : 'Y'; } return $properties; } /** * Show SQL error message * Send notification email if `$RosarioNotifyAddress` or `$RosarioErrorsAddress` set * * @global string $RosarioNotifyAddress or $RosarioErrorsAddress email set in config.inc.php file * @since 4.0 Uses ErrorSendEmail() * @since 4.6 Show SQL query. * * @param string $sql SQL statement. * @param string $failnote Failure Notice. * @param string $additional Additional Information. */ function db_show_error( $sql, $failnote, $additional = '' ) { global $RosarioNotifyAddress, $RosarioErrorsAddress; // TRANSLATION: do NOT translate these since error messages need to stay in English for technical support. ?>


Date

Failure Notice
SQL query