Changeset: 552c5b45f49c for MonetDB URL: https://dev.monetdb.org/hg/MonetDB/rev/552c5b45f49c Modified Files: sql/backends/monet5/vaults/odbc/odbc_loader.c Branch: odbc_loader Log Message:
Added filling the res_exps. For both odbc_relation() and odbc_load() we now call new odbc_query(), which does all the work. diffs (truncated from 316 to 300 lines): diff --git a/sql/backends/monet5/vaults/odbc/odbc_loader.c b/sql/backends/monet5/vaults/odbc/odbc_loader.c --- a/sql/backends/monet5/vaults/odbc/odbc_loader.c +++ b/sql/backends/monet5/vaults/odbc/odbc_loader.c @@ -181,6 +181,53 @@ map_rescol_type(SQLSMALLINT dataType, SQ return sql_bind_subtype(sql->sa, typenm, interval_type, 0); } +static char * +nameofSQLtype(SQLSMALLINT dataType) +{ + /* https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/sql-data-types */ + switch (dataType) { + case SQL_CHAR: return "CHAR"; + case SQL_VARCHAR: return "VARCHAR"; + case SQL_LONGVARCHAR: return "LONG VARCHAR"; + case SQL_WCHAR: return "WCHAR"; + case SQL_WVARCHAR: return "WVARCHAR"; + case SQL_WLONGVARCHAR: return "WLONGVARCHAR"; + case SQL_DECIMAL: return "DECIMAL"; + case SQL_NUMERIC: return "NUMERIC"; + case SQL_SMALLINT: return "SMALLINT"; + case SQL_INTEGER: return "INTEGER"; + case SQL_REAL: return "REAL"; + case SQL_FLOAT: return "FLOAT"; + case SQL_DOUBLE: return "DOUBLE"; + case SQL_BIT: return "BIT"; + case SQL_TINYINT: return "TINYINT"; + case SQL_BIGINT: return "BIGINT"; + case SQL_BINARY: return "BINARY"; + case SQL_VARBINARY: return "VARBINARY"; + case SQL_LONGVARBINARY: return "LONG VARBINARY"; + case SQL_DATETIME: return "DATETIME"; + case SQL_TYPE_DATE: return "DATE"; + case SQL_TYPE_TIME: return "TIME"; + case SQL_TYPE_TIMESTAMP: return "TIMESTAMP"; + case SQL_INTERVAL_MONTH: return "INTERVAL MONTH"; + case SQL_INTERVAL_YEAR: return "INTERVAL YEAR"; + case SQL_INTERVAL_YEAR_TO_MONTH: return "INTERVAL YEAR TO MONTH"; + case SQL_INTERVAL_DAY: return "INTERVAL DAY"; + case SQL_INTERVAL_HOUR: return "INTERVAL HOUR"; + case SQL_INTERVAL_MINUTE: return "INTERVAL MINUTE"; + case SQL_INTERVAL_SECOND: return "INTERVAL SECOND"; + case SQL_INTERVAL_DAY_TO_HOUR: return "INTERVAL DAY TO HOUR"; + case SQL_INTERVAL_DAY_TO_MINUTE: return "INTERVAL DAY TO MINUTE"; + case SQL_INTERVAL_DAY_TO_SECOND: return "INTERVAL DAY TO SECOND"; + case SQL_INTERVAL_HOUR_TO_MINUTE: return "INTERVAL HOUR TO MINUTE"; + case SQL_INTERVAL_HOUR_TO_SECOND: return "INTERVAL HOUR TO SECOND"; + case SQL_INTERVAL_MINUTE_TO_SECOND: return "INTERVAL MINUTE TO SECOND"; + case SQL_GUID: return "GUID"; +/* case SQL_HUGEINT: return "HUGEINT"; 0x4000 (defined in ODBCGlobal.h) */ + default: return "Driver specific type"; + } +} + /* * returns an error string (static or via tmp sa_allocator allocated), NULL on success * @@ -191,12 +238,12 @@ map_rescol_type(SQLSMALLINT dataType, SQ * Fill the list res_exps, with one result expressions per resulting column. */ static str -odbc_relation(mvc *sql, sql_subfunc *f, char *url, list *res_exps, char *aname) +odbc_query(mvc *sql, sql_subfunc *f, char *url, list *res_exps, sql_exp *topn, int caller) { - (void) res_exps; - (void) aname; - bool trace_enabled = true; + (void) topn; + bool trace_enabled = true; /* used for development only */ + /* check received url and extract the ODBC connection string and yhe SQL query */ if (!url || (url && strncasecmp("odbc:", url, 5) != 0)) return "Invalid URI. Must start with 'odbc:'."; @@ -223,6 +270,8 @@ odbc_relation(mvc *sql, sql_subfunc *f, if (trace_enabled) printf("\nExtracted ODBC connection string: %s\nand SQL query: %s\n", odbc_con_str, query); + + /* now we can try to connect to the ODBC driver and execute the SQL query */ SQLRETURN ret = SQL_INVALID_HANDLE; SQLHANDLE env = SQL_NULL_HENV; SQLHANDLE dbc = SQL_NULL_HDBC; @@ -232,17 +281,17 @@ odbc_relation(mvc *sql, sql_subfunc *f, ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { errmsg = "Allocate ODBC ENV handle failed."; - goto failure; + goto finish; } ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) (uintptr_t) SQL_OV_ODBC3, 0); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { errmsg = "SQLSetEnvAttr (SQL_ATTR_ODBC_VERSION ODBC3) failed."; - goto failure; + goto finish; } ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { errmsg = "Allocate ODBC DBC handle failed."; - goto failure; + goto finish; } SQLSMALLINT len = 0; @@ -250,69 +299,112 @@ odbc_relation(mvc *sql, sql_subfunc *f, if (trace_enabled) printf("After SQLDriverConnect(%s) returned %d\n", odbc_con_str, ret); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { - errmsg = "Could not connect. ODBC SQLDriverConnect failed."; - goto failure; + errmsg = "Could not connect. SQLDriverConnect failed."; + goto finish; } ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { errmsg = "Allocate ODBC STMT handle failed."; - goto failure; + goto finish; } ret = SQLExecDirect(stmt, (SQLCHAR *) query, SQL_NTS); if (trace_enabled) printf("After SQLExecDirect(%s) returned %d\n", query, ret); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { - errmsg = "ODBC SQLExecDirect query failed."; - goto failure; + errmsg = "SQLExecDirect query failed."; + goto finish; } SQLSMALLINT nr_cols = 0; ret = SQLNumResultCols(stmt, &nr_cols); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { - errmsg = "ODBC SQLNumResultCols failed."; - goto failure; + errmsg = "SQLNumResultCols failed."; + goto finish; } if (nr_cols <= 0) { errmsg = "ODBC query did not return a resultset."; - goto failure; + goto finish; } if (trace_enabled) printf("Query has %d result columns\n", nr_cols); - char name[2048]; - SQLSMALLINT dataType = 0; - SQLULEN columnSize = 0; - SQLSMALLINT decimalDigits = 0; - list * typelist = sa_list(sql->sa); - list * nameslist = sa_list(sql->sa); - for (SQLUSMALLINT col = 1; col <= (SQLUSMALLINT) nr_cols; col++) { - /* for each result column get name, datatype, size and decdigits */ - ret = SQLDescribeCol(stmt, col, (SQLCHAR *) name, (SQLSMALLINT) sizeof(name), - NULL, &dataType, &columnSize, &decimalDigits, NULL); - if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { - errmsg = "ODBC SQLDescribeCol failed."; - goto failure; + /* when called from odbc_relation() */ + if (caller == 1) { + char tname[1024]; + char cname[1024]; + SQLSMALLINT dataType = 0; + SQLULEN columnSize = 0; + SQLSMALLINT decimalDigits = 0; + sql_subtype * sql_mtype; + list * typelist = sa_list(sql->sa); + list * nameslist = sa_list(sql->sa); + for (SQLUSMALLINT col = 1; col <= (SQLUSMALLINT) nr_cols; col++) { + /* for each result column get name, datatype, size and decdigits */ + ret = SQLDescribeCol(stmt, col, (SQLCHAR *) cname, (SQLSMALLINT) sizeof(cname), + NULL, &dataType, &columnSize, &decimalDigits, NULL); + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { + errmsg = "SQLDescribeCol failed."; + goto finish; + } + if (trace_enabled) + printf("ResCol %d, name: %s, type %d (%s), size %d, decdigits %d\n", + col, cname, (int)dataType, nameofSQLtype(dataType), (int)columnSize, (int)decimalDigits); + list_append(nameslist, cname); + sql_mtype = map_rescol_type(dataType, columnSize, decimalDigits, sql); + list_append(typelist, sql_mtype); + + /* also get the table name for this result column */ + ret = SQLColAttribute(stmt, col, SQL_DESC_TABLE_NAME, (SQLPOINTER) tname, (SQLSMALLINT) sizeof(tname), NULL, NULL); + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { + strcpy(tname, ""); + } + sql_exp *ne = exp_column(sql->sa, tname, cname, sql_mtype, CARD_MULTI, 1, 0, 0); + set_basecol(ne); + ne->alias.label = -(sql->nid++); + list_append(res_exps, ne); } - if (trace_enabled) - printf("ResCol %d, name: %s, type %d, size %d, decdigits %d\n", - col, name, (int)dataType, (int)columnSize, (int)decimalDigits); - list_append(nameslist, name); - list_append(typelist, map_rescol_type(dataType, columnSize, decimalDigits, sql)); + + /* f->tname = sa_strdup(sql->sa, aname); */ + f->res = typelist; + f->coltypes = typelist; + f->colnames = nameslist; + + odbc_loader_t *r = (odbc_loader_t *)sa_alloc(sql->sa, sizeof(odbc_loader_t)); + r->env = env; + r->dbc = dbc; + r->stmt = stmt; + r->nr_cols = nr_cols; + f->sname = (char*)r; /* pass odbc_loader */ + + goto finish; } - f->res = typelist; - f->coltypes = typelist; - f->colnames = nameslist; + /* when called from odbc_load() we can now fetch the data */ + if (caller == 2 && stmt != SQL_NULL_HSTMT) { + // TODO create an internal transient table to store fetched data + // if (mvc_create_table(&t, be->mvc, be->mvc->session->tr->tmp /* misuse tmp schema */, r->tname /*gettable name*/, tt_remote, false, SQL_DECLARED_TABLE, 0, 0, false) != LOG_OK) + + for (SQLUSMALLINT col = 1; col <= (SQLUSMALLINT) nr_cols; col++) { + // TODO for each result column create a buffer and bind it. Also create a BAT column. + // ret = SQLBindCol(stmt, 1, ); + // if (!tp || mvc_create_column(&c, be->mvc, t, name, tp) != LOG_OK) { + } - odbc_loader_t *r = (odbc_loader_t *)sa_alloc(sql->sa, sizeof(odbc_loader_t)); - r->env = env; - r->dbc = dbc; - r->stmt = stmt; - r->nr_cols = nr_cols; - f->sname = (char*)r; /* pass odbc_loader */ - failure: + // repeat fetching data, adding data work table + ret = SQLFetch(stmt); + while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { + // TODO for each result column append to created transient table + for (SQLUSMALLINT col = 1; col <= (SQLUSMALLINT) nr_cols; col++) { + // ret = SQLGetData(stmt, col, ...); + // copy buffer value to BUN and append + } + ret = SQLFetch(stmt); // get data of next row + } + } + + finish: if (query) GDKfree(query); if (odbc_con_str) @@ -354,43 +446,28 @@ odbc_relation(mvc *sql, sql_subfunc *f, return (str)errmsg; } +/* + * returns an error string (static or via tmp sa_allocator allocated), NULL on success + * + * Extend the subfunc f with result columns, ie. + f->res = typelist; + f->coltypes = typelist; + f->colnames = nameslist; use tname if passed, for the relation name + * Fill the list res_exps, with one result expressions per resulting column. + */ +static str +odbc_relation(mvc *sql, sql_subfunc *f, char *url, list *res_exps, char *aname) +{ + (void) aname; + return odbc_query(sql, f, url, res_exps, NULL, 1); +} + static void * odbc_load(void *BE, sql_subfunc *f, char *url, sql_exp *topn) { - (void) url; - (void) topn; - (void) BE; -// backend *be = (backend*)BE; -// mvc *sql = be->mvc; - odbc_loader_t *r = (odbc_loader_t*)f->sname; - SQLHANDLE stmt = r->stmt; - SQLSMALLINT nr_cols = r->nr_cols; - - if (stmt != SQL_NULL_HSTMT) { - SQLRETURN ret; - // TODO create an internal transient table to store fetched data - // if (mvc_create_table(&t, be->mvc, be->mvc->session->tr->tmp /* misuse tmp schema */, r->tname /*gettable name*/, tt_remote, false, SQL_DECLARED_TABLE, 0, 0, false) != LOG_OK) - - for (SQLSMALLINT i = 1; i <= nr_cols; i++) { - // TODO for each result column create a buffer and bind it. Also create a BAT column. - // ret = SQLBindCol(stmt, 1, ); - // if (!tp || mvc_create_column(&c, be->mvc, t, name, tp) != LOG_OK) { - } - - // repeat fetching data, adding data work table - ret = SQLFetch(stmt); - while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { - // TODO for each result column append to created transient table _______________________________________________ checkin-list mailing list -- checkin-list@monetdb.org To unsubscribe send an email to checkin-list-le...@monetdb.org