/** @brief Load the child queries subordinate to a UNION, INTERSECT, or EXCEPT query. @param state Pointer to the query-building context. @param parent ID of the UNION, INTERSECT, or EXCEPT query. @param type_str The type of the query ("UNION", "INTERSECT", or "EXCEPT"). @return If successful, a pointer to a linked list of QSeq, each bearing a pointer to a StoredQ; otherwise NULL. The @a type_str parameter is used only for building error messages. */ static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str ) { QSeq* child_list = NULL; // The ORDER BY is in descending order so that we can build the list by adding to // the head, and it will wind up in the right order. dbi_result result = dbi_conn_queryf( state->dbhandle, "SELECT id, parent_query, seq_no, child_query " "FROM query.query_sequence WHERE parent_query = %d ORDER BY seq_no DESC", parent_id ); if( result ) { if( dbi_result_first_row( result ) ) { int count = 0; while( 1 ) { ++count; QSeq* seq = constructQSeq( state, result ); if( seq ) { PRINT( "Found a child query\n" ); PRINT( "\tid: %d\n", seq->id ); PRINT( "\tparent id: %d\n", seq->parent_query_id ); PRINT( "\tseq_no: %d\n", seq->seq_no ); // Add to the head of the list seq->next = child_list; child_list = seq; } else{ freeQSeqList( child_list ); return NULL; } if( !dbi_result_next_row( result )) break; } if( count < 2 ) { osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state, "%s query # %d has only one child query", type_str, parent_id )); state->error = 1; freeQSeqList( child_list ); return NULL; } } else { osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state, "%s query # %d has no child queries within it", type_str, parent_id )); state->error = 1; return NULL; } } else { const char* msg; int errnum = dbi_conn_error( state->dbhandle, &msg ); osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to query query.query_sequence table: # %d %s", errnum, msg ? msg : "No description available" )); state->error = 1; return NULL; } return child_list; }
/** @brief Load a stored query. @param state Pointer to the query-building context. @param query_id ID of the query in query.stored_query. @return A pointer to the newly loaded StoredQ if successful, or NULL if not. The calling code is responsible for freeing the StoredQ by calling storedQFree(). */ StoredQ* getStoredQuery( BuildSQLState* state, int query_id ) { if( !state ) return NULL; // Check the stack to see if the current query is nested inside itself. If it is, then // abort in order to avoid infinite recursion. If it isn't, then add it to the stack. // (Make sure to pop it off the stack before returning.) if( searchIdStack( state->query_stack, query_id, NULL )) { osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state, "Infinite recursion detected; query # %d is nested within itself", query_id )); state->error = 1; return NULL; } else push_id( &state->query_stack, query_id, NULL ); StoredQ* sq = NULL; dbi_result result = dbi_conn_queryf( state->dbhandle, "SELECT id, type, use_all, use_distinct, from_clause, where_clause, having_clause " "FROM query.stored_query WHERE id = %d;", query_id ); if( result ) { if( dbi_result_first_row( result ) ) { sq = constructStoredQ( state, result ); if( sq ) { PRINT( "Got a query row\n" ); PRINT( "\tid: %d\n", sq->id ); PRINT( "\ttype: %d\n", (int) sq->type ); PRINT( "\tuse_all: %s\n", sq->use_all ? "true" : "false" ); PRINT( "\tuse_distinct: %s\n", sq->use_distinct ? "true" : "false" ); } else osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to build a query for id = %d", query_id )); } else { sqlAddMsg( state, "Stored query not found for id %d", query_id ); } dbi_result_free( result ); } else { const char* msg; int errnum = dbi_conn_error( state->dbhandle, &msg ); osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to query query.stored_query table: #%d %s", errnum, msg ? msg : "No description available" )); } pop_id( &state->query_stack ); return sq; }
/* checks mysql sql_options and adjusts if necessary */ static void adjust_sql_options (dbi_conn connection) { dbi_result result = dbi_conn_query( connection, "SELECT @@sql_mode"); if (result == nullptr) { const char* errmsg; int err = dbi_conn_error(connection, &errmsg); PERR("Unable to read sql_mode %d : %s", err, errmsg); return; } dbi_result_first_row(result); std::string str{dbi_result_get_string_idx(result, 1)}; dbi_result_free(result); if (str.empty()) { const char* errmsg; int err = dbi_conn_error(connection, &errmsg); if (err) PERR("Unable to get sql_mode %d : %s", err, errmsg); else PINFO("Sql_mode isn't set."); return; } PINFO("Initial sql_mode: %s", str.c_str()); if(str.find(SQL_OPTION_TO_REMOVE) == std::string::npos) return; std::string adjusted_str{adjust_sql_options_string(str)}; PINFO("Setting sql_mode to %s", adjusted_str.c_str()); std::string set_str{"SET sql_mode=" + std::move(adjusted_str)}; dbi_result set_result = dbi_conn_query(connection, set_str.c_str()); if (set_result) { dbi_result_free(set_result); } else { const char* errmsg; int err = dbi_conn_error(connection, &errmsg); PERR("Unable to set sql_mode %d : %s", err, errmsg); } }
static SelectItem* getSelectList( BuildSQLState* state, int query_id ) { SelectItem* select_list = NULL; // The ORDER BY is in descending order so that we can build the list by adding to // the head, and it will wind up in the right order. dbi_result result = dbi_conn_queryf( state->dbhandle, "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by " "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id ); if( result ) { if( dbi_result_first_row( result ) ) { while( 1 ) { SelectItem* item = constructSelectItem( state, result ); if( item ) { PRINT( "Found a SELECT item\n" ); PRINT( "\tid: %d\n", item->id ); PRINT( "\tstored_query_id: %d\n", item->stored_query_id ); PRINT( "\tseq_no: %d\n", item->seq_no ); PRINT( "\tcolumn_alias: %s\n", item->column_alias ? item->column_alias : "(none)" ); PRINT( "\tgrouped_by: %d\n", item->grouped_by ); item->next = select_list; select_list = item; } else { osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to build select list for query id #%d", query_id )); selectListFree( select_list ); select_list = NULL; break; } if( !dbi_result_next_row( result ) ) break; }; } } else { const char* msg; int errnum = dbi_conn_error( state->dbhandle, &msg ); osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to query query.select_list table: #%d %s", errnum, msg ? msg : "No description available" )); } return select_list; }
static Expression* getExpression( BuildSQLState* state, int id ) { // Check the stack to see if the current expression is nested inside itself. If it is, // then abort in order to avoid infinite recursion. If it isn't, then add it to the // stack. (Make sure to pop it off the stack before returning.) if( searchIdStack( state->expr_stack, id, NULL )) { osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state, "Infinite recursion detected; expression # %d is nested within itself", id )); state->error = 1; return NULL; } else push_id( &state->expr_stack, id, NULL ); Expression* exp = NULL; dbi_result result = dbi_conn_queryf( state->dbhandle, "SELECT id, type, parenthesize, parent_expr, seq_no, literal, table_alias, " "column_name, left_operand, operator, right_operand, function_id, subquery, cast_type " "FROM query.expression WHERE id = %d;", id ); if( result ) { if( dbi_result_first_row( result ) ) { exp = constructExpression( state, result ); if( exp ) { PRINT( "Got an expression\n" ); PRINT( "\tid = %d\n", exp->id ); PRINT( "\ttype = %d\n", exp->type ); PRINT( "\tparenthesize = %d\n", exp->parenthesize ); PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" ); } else osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to construct an Expression for id = %d", id )); } } else { const char* msg; int errnum = dbi_conn_error( state->dbhandle, &msg ); osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to query query.expression table: #%d %s", errnum, msg ? msg : "No description available" )); } pop_id( &state->expr_stack ); return exp; }
/** @brief Build a list of joined relations. @param state Pointer to the query-building context. @param id ID of the parent relation. @return A pointer to the first in a linked list of FromRelations, if there are any; or NULL if there aren't any, or in case of an error. Look for relations joined directly to the parent relation, and make a list of them. */ static FromRelation* getJoinList( BuildSQLState* state, int id ) { FromRelation* join_list = NULL; // The ORDER BY is in descending order so that we can build the list by adding to // the head, and it will wind up in the right order. dbi_result result = dbi_conn_queryf( state->dbhandle, "SELECT id, type, table_name, class_name, subquery, function_call, " "table_alias, parent_relation, seq_no, join_type, on_clause " "FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id ); if( result ) { if( dbi_result_first_row( result ) ) { while( 1 ) { FromRelation* relation = constructFromRelation( state, result ); if( relation ) { PRINT( "Found a joined relation\n" ); PRINT( "\tjoin_type: %d\n", relation->join_type ); PRINT( "\ttable_name: %s\n", relation->table_name ); relation->next = join_list; join_list = relation; } else { osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to build join list for from relation id #%d", id )); joinListFree( join_list ); join_list = NULL; break; } if( !dbi_result_next_row( result ) ) break; }; } } else { const char* msg; int errnum = dbi_conn_error( state->dbhandle, &msg ); osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to query query.from_relation table for join list: #%d %s", errnum, msg ? msg : "No description available" )); } return join_list; }
static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) { OrderItem* ord_list = NULL; // The ORDER BY is in descending order so that we can build the list by adding to // the head, and it will wind up in the right order. dbi_result result = dbi_conn_queryf( state->dbhandle, "SELECT id, stored_query, seq_no, expression " "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id ); if( result ) { if( dbi_result_first_row( result ) ) { while( 1 ) { OrderItem* item = constructOrderItem( state, result ); if( item ) { PRINT( "Found an ORDER BY item\n" ); item->next = ord_list; ord_list = item; } else { osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to build ORDER BY item for query id #%d", query_id )); orderItemListFree( ord_list ); ord_list = NULL; break; } if( !dbi_result_next_row( result ) ) break; }; } } else { const char* msg; int errnum = dbi_conn_error( state->dbhandle, &msg ); osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to query query.order_by_list table: #%d %s", errnum, msg ? msg : "No description available" )); } return ord_list; }
static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */ udb_query_t *q, udb_query_preparation_area_t *prep_area) { const char *statement; dbi_result res; size_t column_num; char **column_names; char **column_values; int status; size_t i; /* Macro that cleans up dynamically allocated memory and returns the * specified status. */ #define BAIL_OUT(status) \ if (column_names != NULL) { sfree (column_names[0]); sfree (column_names); } \ if (column_values != NULL) { sfree (column_values[0]); sfree (column_values); } \ if (res != NULL) { dbi_result_free (res); res = NULL; } \ return (status) column_names = NULL; column_values = NULL; statement = udb_query_get_statement (q); assert (statement != NULL); res = dbi_conn_query (db->connection, statement); if (res == NULL) { char errbuf[1024]; ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): " "dbi_conn_query failed: %s", db->name, udb_query_get_name (q), cdbi_strerror (db->connection, errbuf, sizeof (errbuf))); BAIL_OUT (-1); } else /* Get the number of columns */ { unsigned int db_status; db_status = dbi_result_get_numfields (res); if (db_status == DBI_FIELD_ERROR) { char errbuf[1024]; ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): " "dbi_result_get_numfields failed: %s", db->name, udb_query_get_name (q), cdbi_strerror (db->connection, errbuf, sizeof (errbuf))); BAIL_OUT (-1); } column_num = (size_t) db_status; DEBUG ("cdbi_read_database_query (%s, %s): There are %zu columns.", db->name, udb_query_get_name (q), column_num); } /* Allocate `column_names' and `column_values'. {{{ */ column_names = calloc (column_num, sizeof (*column_names)); if (column_names == NULL) { ERROR ("dbi plugin: calloc failed."); BAIL_OUT (-1); } column_names[0] = calloc (column_num, DATA_MAX_NAME_LEN); if (column_names[0] == NULL) { ERROR ("dbi plugin: calloc failed."); BAIL_OUT (-1); } for (i = 1; i < column_num; i++) column_names[i] = column_names[i - 1] + DATA_MAX_NAME_LEN; column_values = calloc (column_num, sizeof (*column_values)); if (column_values == NULL) { ERROR ("dbi plugin: calloc failed."); BAIL_OUT (-1); } column_values[0] = calloc (column_num, DATA_MAX_NAME_LEN); if (column_values[0] == NULL) { ERROR ("dbi plugin: calloc failed."); BAIL_OUT (-1); } for (i = 1; i < column_num; i++) column_values[i] = column_values[i - 1] + DATA_MAX_NAME_LEN; /* }}} */ /* Copy the field names to `column_names' */ for (i = 0; i < column_num; i++) /* {{{ */ { const char *column_name; column_name = dbi_result_get_field_name (res, (unsigned int) (i + 1)); if (column_name == NULL) { ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): " "Cannot retrieve name of field %zu.", db->name, udb_query_get_name (q), i + 1); BAIL_OUT (-1); } sstrncpy (column_names[i], column_name, DATA_MAX_NAME_LEN); } /* }}} for (i = 0; i < column_num; i++) */ udb_query_prepare_result (q, prep_area, (db->host ? db->host : hostname_g), /* plugin = */ "dbi", db->name, column_names, column_num, /* interval = */ (db->interval > 0) ? db->interval : 0); /* 0 = error; 1 = success; */ status = dbi_result_first_row (res); /* {{{ */ if (status != 1) { char errbuf[1024]; ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): " "dbi_result_first_row failed: %s. Maybe the statement didn't " "return any rows?", db->name, udb_query_get_name (q), cdbi_strerror (db->connection, errbuf, sizeof (errbuf))); udb_query_finish_result (q, prep_area); BAIL_OUT (-1); } /* }}} */ /* Iterate over all rows and call `udb_query_handle_result' with each list of * values. */ while (42) /* {{{ */ { status = 0; /* Copy the value of the columns to `column_values' */ for (i = 0; i < column_num; i++) /* {{{ */ { status = cdbi_result_get_field (res, (unsigned int) (i + 1), column_values[i], DATA_MAX_NAME_LEN); if (status != 0) { ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): " "cdbi_result_get_field (%zu) failed.", db->name, udb_query_get_name (q), i + 1); status = -1; break; } } /* }}} for (i = 0; i < column_num; i++) */ /* If all values were copied successfully, call `udb_query_handle_result' * to dispatch the row to the daemon. */ if (status == 0) /* {{{ */ { status = udb_query_handle_result (q, prep_area, column_values); if (status != 0) { ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): " "udb_query_handle_result failed.", db->name, udb_query_get_name (q)); } } /* }}} */ /* Get the next row from the database. */ status = dbi_result_next_row (res); /* {{{ */ if (status != 1) { if (dbi_conn_error (db->connection, NULL) != 0) { char errbuf[1024]; WARNING ("dbi plugin: cdbi_read_database_query (%s, %s): " "dbi_result_next_row failed: %s.", db->name, udb_query_get_name (q), cdbi_strerror (db->connection, errbuf, sizeof (errbuf))); } break; } /* }}} */ } /* }}} while (42) */ /* Tell the db query interface that we're done with this query. */ udb_query_finish_result (q, prep_area); /* Clean up and return `status = 0' (success) */ BAIL_OUT (0); #undef BAIL_OUT } /* }}} int cdbi_read_database_query */
static FromRelation* getFromRelation( BuildSQLState* state, int id ) { FromRelation* fr = NULL; dbi_result result = dbi_conn_queryf( state->dbhandle, "SELECT id, type, table_name, class_name, subquery, function_call, " "table_alias, parent_relation, seq_no, join_type, on_clause " "FROM query.from_relation WHERE id = %d;", id ); if( result ) { if( dbi_result_first_row( result ) ) { fr = constructFromRelation( state, result ); if( fr ) { PRINT( "Got a from_relation row\n" ); PRINT( "\tid: %d\n", fr->id ); PRINT( "\ttype: %d\n", (int) fr->type ); PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" ); PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" ); PRINT( "\tsubquery_id: %d\n", fr->subquery_id ); PRINT( "\tfunction_call_id: %d\n", fr->function_call_id ); PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" ); PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id ); PRINT( "\tseq_no: %d\n", fr->seq_no ); PRINT( "\tjoin_type = %d\n", fr->join_type ); // Check the stack to see if the current from clause is nested inside itself. // If it is, then abort in order to avoid infinite recursion. If it isn't, // then add it to the stack. (Make sure to pop it off the stack before // returning.) const char* effective_alias = fr->table_alias; if( !effective_alias ) effective_alias = fr->class_name; const IdNode* node = searchIdStack( state->from_stack, id, effective_alias ); if( node ) { if( node->id == id ) osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state, "Infinite recursion detected; from clause # %d is nested " "within itself", id )); else osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state, "Conflicting nested table aliases \"%s\" in from clause # %d", effective_alias, node->id )); state->error = 1; return NULL; } else push_id( &state->from_stack, id, effective_alias ); } else osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to build a FromRelation for id = %d", id )); } else { osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state, "FROM relation not found for id = %d", id )); } dbi_result_free( result ); } else { const char* msg; int errnum = dbi_conn_error( state->dbhandle, &msg ); osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Unable to query query.from_relation table: #%d %s", errnum, msg ? msg : "No description available" )); } if( fr ) pop_id( &state->from_stack ); return fr; }