#include "pch_msc.hpp" #include "libaquery.h" #include #include #include "monetdb_conn.h" #include "monetdbe.h" #include "table.h" #include #ifdef _WIN32 #include "winhelper.h" #include "threading.h" #undef min #undef max #else #include #include #include #include #endif // _WIN32 #ifdef ERROR #undef ERROR #endif #ifdef static_assert #undef static_assert #endif constexpr const char* monetdbe_type_str[] = { "monetdbe_bool", "monetdbe_int8_t", "monetdbe_int16_t", "monetdbe_int32_t", "monetdbe_int64_t", #ifdef HAVE_HGE "monetdbe_int128_t", #endif "monetdbe_size_t", "monetdbe_float", "monetdbe_double", "monetdbe_str", "monetdbe_blob", "monetdbe_date", "monetdbe_time", "monetdbe_timestamp", // should be last: "monetdbe_type_unknown" } ; inline constexpr static unsigned char monetdbe_type_szs[] = { sizeof(monetdbe_column_bool::null_value), sizeof(monetdbe_column_int8_t::null_value), sizeof(monetdbe_column_int16_t::null_value), sizeof(monetdbe_column_int32_t::null_value), sizeof(monetdbe_column_int64_t::null_value), #ifdef HAVE_HGE sizeof(monetdbe_column_int128_t::null_value), #endif sizeof(monetdbe_column_size_t::null_value), sizeof(monetdbe_column_float::null_value), sizeof(monetdbe_column_double::null_value), sizeof(monetdbe_column_str::null_value), sizeof(monetdbe_column_blob::null_value), sizeof(monetdbe_data_date), sizeof(monetdbe_data_time), sizeof(monetdbe_data_timestamp), // should be last: 1 }; namespace types{ constexpr const Type_t monetdbe_type_aqtypes[] = { ABOOL, AINT8, AINT16, AINT32, AINT64, #ifdef HAVE_HGE AINT128, #endif AUINT64, AFLOAT, ADOUBLE, ASTR, // blob? AINT64, ADATE, ATIME, ATIMESTAMP, ERROR }; } MonetdbServer::MonetdbServer(Context* cxt) { this->DataSourceType = BACKEND_MonetDB; if (cxt){ connect(cxt); } } void MonetdbServer::connect(Context *cxt){ auto server = static_cast(this->server); if (cxt){ cxt->alt_server[DataSourceType] = this; this->cxt = cxt; } else{ puts("Error: Empty context."); return; } if (server){ printf("Error: MonetdbServer %p already connected. Restart? (Y/n). \n", server); char c[50]; std::cin.getline(c, 49); for(int i = 0; i < 50; ++i) { if (!c[i] || c[i] == 'y' || c[i] == 'Y') { monetdbe_close(*server); free(*server); this->server = nullptr; break; } else if(c[i]&&!(c[i] == ' ' || c[i] == '\t')) break; } } server = (monetdbe_database*)malloc(sizeof(monetdbe_database)); monetdbe_options ops; AQ_ZeroMemory(ops); ops.nr_threads = std::thread::hardware_concurrency(); auto ret = monetdbe_open(server, nullptr, &ops); if (ret == 0){ status = true; this->server = server; } else{ if(server) free(server); this->server = nullptr; status = false; puts(ret == -1 ? "Allocation Error." : "Internal Database Error."); } } void MonetdbServer::exec(const char* q){ auto server = static_cast(this->server); auto _res = static_cast(this->res); monetdbe_cnt _cnt = 0; auto qresult = monetdbe_query(*server, const_cast(q), &_res, &_cnt); if (_res != nullptr){ this->cnt = _res->nrows; this->res = _res; } if (qresult != nullptr){ printf("Execution Failed. %s\n", qresult); last_error = qresult; } } bool MonetdbServer::haserror(){ if (last_error){ puts(last_error); last_error = nullptr; return true; } else{ return false; } } void MonetdbServer::print_results(const char* sep, const char* end, uint32_t) { if (!haserror()){ auto _res = static_cast (res); const auto& ncols = _res->ncols; monetdbe_column** cols = static_cast(malloc(sizeof(monetdbe_column*) * ncols)); std::string* printf_string = new std::string[ncols]; const char** col_data = static_cast (malloc(sizeof(char*) * ncols)); uint8_t* szs = static_cast(alloca(ncols)); std::string header_string = ""; const char* err_msg = nullptr; for(uint32_t i = 0; i < ncols; ++i){ err_msg = monetdbe_result_fetch(_res, &cols[i], i); printf_string[i] = std::string(types::printf_str[types::monetdbe_type_aqtypes[cols[i]->type]]) + (i < ncols - 1 ? sep : ""); puts(printf_string[i].c_str()); puts(monetdbe_type_str[cols[i]->type]); col_data[i] = static_cast(cols[i]->data); szs [i] = monetdbe_type_szs[cols[i]->type]; header_string = header_string + cols[i]->name + sep + '|' + sep; if (err_msg) [[unlikely]] puts(err_msg); } if (const size_t l_sep = strlen(sep) + 1; header_string.size() >= l_sep) header_string.resize(header_string.size() - l_sep); header_string += end + std::string(header_string.size(), '=') + end; fputs(header_string.c_str(), stdout); for(uint64_t i = 0; i < cnt; ++i){ for(uint32_t j = 0; j < ncols; ++j){ printf(printf_string[j].c_str(), *((void**)col_data[j])); col_data[j] += szs[j]; } fputs(end, stdout); } free(cols); delete[] printf_string; free(col_data); } } void MonetdbServer::close(){ if(this->server){ auto server = static_cast(this->server); monetdbe_close(*server); free(server); this->server = nullptr; } } void* MonetdbServer::getCol(int col_idx, int){ if(res){ auto _res = static_cast(this->res); auto err_msg = monetdbe_result_fetch(_res, reinterpret_cast(&ret_col), col_idx); if(err_msg == nullptr) { auto _ret_col = static_cast(this->ret_col); cnt = _ret_col->count; printf("Dbg: Getting col %s, type: %s\n", _ret_col->name, monetdbe_type_str[_ret_col->type]); return _ret_col->data; } else{ printf("Error fetching result: %s\n", err_msg); } } else{ puts("Error: No result."); } return nullptr; } #define AQ_MONETDB_FETCH(X) case monetdbe_##X: \ return (long long)((X *)(_ret_col->data))[0]; long long MonetdbServer::getFirstElement() { if(!this->haserror() && res) { auto _res = static_cast(this->res); auto err_msg = monetdbe_result_fetch(_res, reinterpret_cast(&ret_col), 0); if(err_msg == nullptr) { auto _ret_col = static_cast(this->ret_col); cnt = _ret_col->count; if(cnt > 0) { switch(_ret_col->type) { AQ_MONETDB_FETCH(bool) AQ_MONETDB_FETCH(int8_t) AQ_MONETDB_FETCH(int16_t) AQ_MONETDB_FETCH(int32_t) AQ_MONETDB_FETCH(int64_t) #ifdef HAVE_HGE case monetdbe_int128_t: return (long long)((__int128_t *)(_ret_col->data))[0]; #endif AQ_MONETDB_FETCH(size_t) AQ_MONETDB_FETCH(float) AQ_MONETDB_FETCH(double) case monetdbe_str: return ((const char **)(_ret_col->data))[0][0] == '\0'; default: printf("Error, non-primitive result: Getting col %s, type: %s\n", _ret_col->name, monetdbe_type_str[_ret_col->type]); return 0; } } } else { printf("Error fetching result: %s\n", err_msg); } } else { puts("Error: No result."); } return 0; } void MonetdbServer::getDSTable(const char* name, void* tbl) { TableInfo *table = static_cast*>(tbl); void*** cols = static_cast(alloca(table->n_cols * sizeof(void**))); printf("\tncols: %d\n", table->n_cols); for (int i = 0; i < table->n_cols; ++i) { cols[i] = static_cast( static_cast( &(table->colrefs[i].container) ) ); } monetdbe_get_cols(*(void**)(this->server), name, cols, table->n_cols); } MonetdbServer::~MonetdbServer(){ close(); } bool MonetdbServer::havehge() { #if defined(_MONETDBE_LIB_) and defined(HAVE_HGE) // puts("true"); return HAVE_HGE; #else // puts("false"); return false; #endif } using prt_fn_t = char* (*)(void*, char*); constexpr prt_fn_t monetdbe_prtfns[] = { aq_to_chars, aq_to_chars, aq_to_chars, aq_to_chars, aq_to_chars, #if __SIZEOF_INT128__ aq_to_chars<__int128_t>, #endif aq_to_chars, aq_to_chars, aq_to_chars, aq_to_chars, aq_to_chars, aq_to_chars, aq_to_chars, aq_to_chars, // should be last: aq_to_chars }; constexpr uint32_t output_buffer_size = 65536; void print_monetdb_results(void* _srv, const char* sep = " ", const char* end = "\n", uint32_t limit = std::numeric_limits::max()) { auto srv = static_cast(_srv); if (!srv->haserror() && srv->cnt && limit) { char buffer[output_buffer_size]; auto _res = static_cast (srv->res); const auto ncols = _res->ncols; monetdbe_column** cols = static_cast(malloc(sizeof(monetdbe_column*) * ncols)); prt_fn_t *prtfns = (prt_fn_t*) alloca(sizeof(prt_fn_t) * ncols); char** col_data = static_cast (alloca(sizeof(char*) * ncols)); uint8_t* szs = static_cast(alloca(ncols)); std::string header_string = ""; const char* err_msg = nullptr; const size_t l_sep = strlen(sep); const size_t l_end = strlen(end); char* _buffer = buffer; const auto cnt = srv->cnt < limit? srv->cnt : limit; for(uint32_t i = 0; i < ncols; ++i){ err_msg = monetdbe_result_fetch(_res, &cols[i], i); if(err_msg) { goto cleanup; } col_data[i] = static_cast(cols[i]->data); prtfns[i] = monetdbe_prtfns[cols[i]->type]; szs [i] = monetdbe_type_szs[cols[i]->type]; header_string = header_string + cols[i]->name + sep + '|' + sep; } if(l_sep > 512 || l_end > 512) { puts("Error: separator or end string too long"); goto cleanup; } if (header_string.size() >= l_sep + 1) header_string.resize(header_string.size() - l_sep - 1); header_string += end + std::string(header_string.size(), '=') + end; fputs(header_string.c_str(), stdout); for(uint64_t i = 0; i < cnt; ++i){ for(uint32_t j = 0; j < ncols; ++j){ //copy the field to buf _buffer = prtfns[j](col_data[j], _buffer); if (j != ncols - 1){ memcpy(_buffer, sep, l_sep); _buffer += l_sep; } col_data[j] += szs[j]; } memcpy(_buffer, end, l_end); _buffer += l_end; if(output_buffer_size - (_buffer - buffer) <= 1024){ fwrite(buffer, 1, _buffer - buffer, stdout); _buffer = buffer; } } memcpy(_buffer, end, l_end); _buffer += l_end; if (_buffer != buffer) fwrite(buffer, 1, _buffer - buffer, stdout); cleanup: free(cols); } } int ExecuteStoredProcedureEx(const StoredProcedure *p, Context* cxt){ auto server = static_cast(cxt->alt_server[BACKEND_MonetDB]); int ret = 0; bool return_from_procedure = false; void* handle = nullptr; uint32_t procedure_module_cursor = 0; for(uint32_t i = 0; i < p->cnt; ++i) { switch(p->queries[i][0]){ puts(p->queries[i]); case 'Q': { server->exec(p->queries[i] + 1); ret = int(server->getFirstElement()); } break; case 'P': { auto c = code_snippet(dlsym(handle, p->queries[i] + 1)); ret = c(cxt); } break; case 'N': { if(procedure_module_cursor < p->postproc_modules) handle = p->__rt_loaded_modules[procedure_module_cursor++]; } break; case 'T' : { if (p->queries[i][1] == 'N') { cxt->ct_host->execute_trigger(p->queries[i] + 2); } } break; case 'O': { uint32_t limit; memcpy(&limit, p->queries[i] + 1, sizeof(uint32_t)); if (limit == 0) continue; print_monetdb_results(server, " ", "\n", limit); } break; default: printf("Warning Q%u: unrecognized command %c.\n", i, p->queries[i][0]); } } return ret; } int execTriggerPayload(void* args) { auto spp = (StoredProcedurePayload*)(args); puts("exec trigger"); auto ret = ExecuteStoredProcedureEx(spp->p, spp->cxt); delete spp; return ret; } int execTriggerPayloadCond(void* args) { int ret = 0; auto spp = (StoredProcedurePayloadCond*)(args); if(ExecuteStoredProcedureEx(spp->condition, spp->cxt) != 0) ret = ExecuteStoredProcedureEx(spp->action, spp->cxt); free(spp->condition); free(spp->action); delete spp; return ret; }