Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge PostgreSQL fixes branch

This covers several fixes to (re-)enable support for pgsql database in
master branch, which has been broken since upgrade to unpatched ADOdb
5.11 several years ago. With these, PostgreSQL should now be
operational:

- Correct handling for booleans, including fix of installer/upgrade
  process to convert pre 1.1.0 instances SMALLINT columns to BOOLEAN
- Finally fixing the broken Travis builds. This required new DB API
  calls to push/pop the parameter count to a stack, to enable concurrent
  build of multiple queries (required as pgsql uses positional
  parameters). Initial implementation 124f79e was procedural, then I moved
  this to a simple class in d09d5ad
- ADOdb update to latest development head (includes a required fix to
  ADODB_postgres64->Param() )

Also includes .travis.yml changes to stop ignoring pgsql build failures.

Pull request #143
  • Loading branch information...
commit b5194d98fd7512506be8384e2ac05ec499e05f8e 2 parents 2d40728 + 37e5732
@dregad dregad authored
View
2  .travis.yml
@@ -27,9 +27,7 @@ branches:
- master-2.0.x
matrix:
- # Until #14398 is fixed, PostgreSQL should not cause entire build to fail
allow_failures:
- - env: DB=pgsql
# Notifications
View
52 admin/install.php
@@ -838,12 +838,62 @@ function print_test( $p_test_description, $p_result, $p_hard_fail = true, $p_mes
}
}
+ $dict = NewDataDictionary( $g_db );
+
+ # Special processing for specific schema versions
+ # This allows execution of additional install steps, which are
+ # not a Mantis schema upgrade but nevertheless required due to
+ # changes in the code
+
+ if( $t_last_update > 51 && $t_last_update < 189 ) {
+ # Since MantisBT 1.1.0 / ADOdb 4.96 (corresponding to schema 51)
+ # 'L' columns are BOOLEAN instead of SMALLINT
+ # Check for any DB discrepancies and update columns if needed
+ $t_bool_columns = check_pgsql_bool_columns();
+ if( $t_bool_columns !== true ) {
+ # Some columns need converting
+ $t_msg = "PostgreSQL: check Boolean columns' actual type";
+ if( is_array( $t_bool_columns ) ) {
+ print_test(
+ $t_msg,
+ count( $t_bool_columns ) == 0,
+ false,
+ count( $t_bool_columns ) . ' columns must be converted to BOOLEAN'
+ );
+ } else {
+ # We did not get an array => error occured
+ print_test( $t_msg, false, true, $t_bool_columns );
+ }
+
+ # Convert the columns
+ foreach( $t_bool_columns as $t_row ) {
+ extract( $t_row, EXTR_PREFIX_ALL, 'v' );
+ $t_null = $v_is_nullable ? 'NULL' : 'NOT NULL';
+ $t_default = is_null( $v_column_default ) ? 'NULL' : $v_column_default;
+ $t_sqlarray = $dict->AlterColumnSQL(
+ $v_table_name,
+ "$v_column_name L $t_null DEFAULT $t_default"
+ );
+ print_test(
+ "Converting column $v_table_name.$v_column_name to BOOLEAN",
+ 2 == $dict->ExecuteSQLArray( $t_sqlarray, false ),
+ true,
+ print_r( $t_sqlarray, true )
+ );
+ if( $g_failed ) {
+ # Error occured, bail out
+ break;
+ }
+ }
+ }
+ }
+ # End of special processing for specific schema versions
+
while(( $i <= $lastid ) && !$g_failed ) {
if( !$f_log_queries ) {
echo '<tr><td bgcolor="#ffffff">';
}
- $dict = @NewDataDictionary( $g_db );
$t_sql = true;
$t_target = $upgrade[$i][1][0];
View
4 core/custom_field_api.php
@@ -116,7 +116,7 @@ function custom_field_cache_row( $p_field_id, $p_trigger_errors = true ) {
$t_result = db_query_bound( $t_query, array( $p_field_id ) );
$t_row = db_fetch_array( $t_result );
-
+
if( !$t_row ) {
if( $p_trigger_errors ) {
error_parameters( 'Custom ' . $p_field_id );
@@ -632,6 +632,8 @@ function custom_field_get_linked_ids( $p_project_id = ALL_PROJECTS ) {
$t_custom_field_table = db_get_table( 'custom_field' );
$t_custom_field_project_table = db_get_table( 'custom_field_project' );
+ db_param_push();
+
if( ALL_PROJECTS == $p_project_id ) {
$t_project_user_list_table = db_get_table( 'project_user_list' );
$t_project_table = db_get_table( 'project' );
View
78 core/database_api.php
@@ -73,10 +73,58 @@
}
/**
- * Tracks the query parameter count for use with db_aparam:().
- * @global int $g_db_param_count
+ * Mantis Database Parameters Count class
+ * Stores the current parameter count, provides method to generate parameters
+ * and a simple stack mechanism to enable the caller to build multiple queries
+ * concurrently on RDBMS using positional parameters (e.g. PostgreSQL)
+ * @package MantisBT
+ * @subpackage classes
*/
-$g_db_param_count = 0;
+class MantisDbParam {
+ /** Current parameter count */
+ public $count = 0;
+
+ /** Parameter count stack */
+ private $stack = array();
+
+ /**
+ * Generate a string to insert a parameter into a database query string
+ * @return string 'wildcard' matching a parameter in correct ordered format for the current database.
+ */
+ public function assign() {
+ global $g_db;
+ return $g_db->Param( $this->count++ );
+ }
+
+ /**
+ * Pushes current parameter count onto stack and resets its value to 0
+ */
+ public function push() {
+ $this->stack[] = $this->count;
+ $this->count = 0;
+ }
+
+ /**
+ * Pops the previous value of param count from the stack
+ * This function is called by {@see db_query_bound()} and should not need
+ * to be executed directly
+ */
+ public function pop() {
+ global $g_db;
+
+ $this->count = (int)array_pop( $this->stack );
+ if( db_is_pgsql() ) {
+ # Manually reset the ADOdb param number to the value we just popped
+ $g_db->_pnum = $this->count;
+ }
+ }
+}
+
+/**
+ * Tracks the query parameter count
+ * @global object $g_db_param
+ */
+$g_db_param = new MantisDbParam();
/**
* Open a connection to the database.
@@ -275,6 +323,7 @@ function db_check_identifier_size( $p_identifier ) {
/**
* execute query, requires connection to be opened
* An error will be triggered if there is a problem executing the query.
+ * This will pop the database parameter stack {@see MantisDbParam} after a successful execution
* @global array of previous executed queries for profiling
* @global adodb database connection object
* @global boolean indicating whether queries array is populated
@@ -285,7 +334,7 @@ function db_check_identifier_size( $p_identifier ) {
* @return ADORecordSet|bool adodb result set or false if the query failed.
*/
function db_query_bound( $p_query, $arr_parms = null, $p_limit = -1, $p_offset = -1 ) {
- global $g_queries_array, $g_db, $g_db_log_queries, $g_db_param_count;
+ global $g_queries_array, $g_db, $g_db_log_queries, $g_db_param;
$t_db_type = config_get_global( 'db_type' );
@@ -368,28 +417,33 @@ function db_query_bound( $p_query, $arr_parms = null, $p_limit = -1, $p_offset =
array_push( $g_queries_array, array( '', $t_elapsed ) );
}
- # We can't reset the counter because we have queries being built
- # and executed while building bigger queries in filter_api. -jreese
- # $g_db_param_count = 0;
-
if( !$t_result ) {
db_error( $p_query );
trigger_error( ERROR_DB_QUERY_FAILED, ERROR );
return false;
} else {
+ $g_db_param->pop();
return $t_result;
}
}
/**
* Generate a string to insert a parameter into a database query string
- * @return string 'wildcard' matching a paramater in correct ordered format for the current database.
+ * @return string 'wildcard' matching a parameter in correct ordered format for the current database.
*/
function db_param() {
- global $g_db;
- global $g_db_param_count;
+ global $g_db_param;
+ return $g_db_param->assign();
+}
- return $g_db->Param( $g_db_param_count++ );
+/**
+ * Pushes current parameter count onto stack and resets its value
+ * Allows the caller to build multiple queries concurrently on RDBMS using
+ * positional parameters (e.g. PostgreSQL)
+ */
+function db_param_push() {
+ global $g_db_param;
+ $g_db_param->push();
}
/**
View
59 core/install_helper_functions_api.php
@@ -82,6 +82,65 @@ function db_unixtimestamp( $p_date = null, $p_gmt = false ) {
}
/**
+ * Check PostgreSQL boolean columns' type in the DB
+ * Verifies that columns defined as type "L" (logical) in the Mantis schema
+ * have the correct type in the underlying database.
+ * The ADOdb library bundled with MantisBT releases prior to 1.1.0 (schema
+ * version 51) created type "L" columns in PostgreSQL as SMALLINT, whereas later
+ * versions created them as BOOLEAN.
+ * @return mixed true if columns check OK
+ * error message string if errors occured
+ * array of invalid columns otherwise (empty if all columns check OK)
+ */
+function check_pgsql_bool_columns() {
+ global $f_db_type, $f_database_name;
+ global $g_db;
+
+ # Only applies to PostgreSQL
+ if( $f_db_type != 'pgsql' ) {
+ return true;
+ }
+
+ # Build the list of "L" type columns as of schema version 51
+ $t_bool_columns = array(
+ 'bug' => array( 'sticky' ),
+ 'custom_field' => array( 'advanced', 'require_report', 'require_update', 'display_report', 'display_update', 'require_resolved', 'display_resolved', 'display_closed', 'require_closed' ),
+ 'filters' => array( 'is_public' ),
+ 'news' => array( 'announcement' ),
+ 'project' => array( 'enabled' ),
+ 'project_version' => array( 'released' ),
+ 'sponsorship' => array( 'paid' ),
+ 'user_pref' => array( 'advanced_report', 'advanced_view', 'advanced_update', 'redirect_delay', 'email_on_new', 'email_on_assigned', 'email_on_feedback', 'email_on_resolved', 'email_on_closed', 'email_on_reopened', 'email_on_bugnote', 'email_on_status', 'email_on_priority' ),
+ 'user' => array( 'enabled', 'protected' ),
+ );
+
+ # Generate SQL to check columns against schema
+ $t_where = '';
+ foreach( $t_bool_columns as $t_table_name => $t_columns ) {
+ $t_table = db_get_table( $t_table_name );
+ $t_where .= "table_name = '$t_table' AND column_name IN ( '"
+ . implode($t_columns, "', '")
+ . "' ) OR\n";
+ }
+ $sql = "SELECT table_name, column_name, data_type, column_default, is_nullable
+ FROM information_schema.columns
+ WHERE
+ table_catalog = '$f_database_name' AND
+ data_type <> 'boolean' AND
+ (\n" . rtrim( $t_where, " OR\n" ) . "\n)";
+
+ $t_result = @$g_db->Execute( $sql );
+ if( $t_result === false ) {
+ return 'Unable to check information_schema';
+ } else if( $t_result->RecordCount() == 0 ) {
+ return array();
+ }
+
+ # Some columns are not BOOLEAN type, return the list
+ return $t_result->GetArray();
+}
+
+/**
* Set the value of $g_db_log_queries as specified
* This is used by install callback functions to ensure that only the relevant
* queries are logged
View
2  core/user_api.php
@@ -1033,6 +1033,8 @@ function user_get_accessible_subprojects( $p_user_id, $p_project_id, $p_show_dis
$t_project_user_list_table = db_get_table( 'project_user_list' );
$t_project_hierarchy_table = db_get_table( 'project_hierarchy' );
+ db_param_push();
+
if( access_has_global_level( config_get( 'private_project_threshold' ), $p_user_id ) ) {
$t_enabled_clause = $p_show_disabled ? '' : 'p.enabled = ' . db_param() . ' AND';
$query = "SELECT DISTINCT p.id, p.name, ph.parent_id
2  library/adodb
@@ -1 +1 @@
-Subproject commit 1208307846b90c9d5594f3163451c5039c45bbef
+Subproject commit c1abd33cf8f4c67a98f559a0028f39d8194b21f0
View
5 scripts/travis_before_script.sh
@@ -5,6 +5,7 @@
# Global variables initialization
HOSTNAME=localhost
+PORT=80
MANTIS_DB_NAME=bugtracker
MANTIS_BOOTSTRAP=tests/bootstrap.php
@@ -82,7 +83,7 @@ else
# get path of PHP as the path is not in $PATH for sudo
myphp=$(which php)
# sudo needed for port 80
- sudo $myphp -S $HOSTNAME:80 &
+ sudo $myphp -S $HOSTNAME:$PORT &
fi
# wait until server is up
@@ -114,7 +115,7 @@ do
done
# trigger installation
-curl --data "${query_string:1}" http://$HOSTNAME/admin/install.php
+curl --data "${query_string:1}" http://$HOSTNAME:$PORT/admin/install.php
# -----------------------------------------------------------------------------
Please sign in to comment.
Something went wrong with that request. Please try again.