diff --git a/driver/connect.c b/driver/connect.c index a838b177..43226c89 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -1402,6 +1402,9 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) /* "apply TZ" param for time conversions */ dbc->apply_tz = wstr2bool(&attrs->apply_tz); INFOH(dbc, "apply TZ: %s.", dbc->apply_tz ? "true" : "false"); + /* early execution */ + dbc->early_exec = wstr2bool(&attrs->early_exec); + INFOH(dbc, "early execution: %s.", dbc->early_exec ? "true" : "false"); /* how to print the floats? */ assert(1 <= attrs->sci_floats.cnt); /* default should apply */ diff --git a/driver/defs.h b/driver/defs.h index 8c9b594c..bd3b3f4e 100644 --- a/driver/defs.h +++ b/driver/defs.h @@ -173,6 +173,8 @@ #define ESODBC_DEF_TRACE_LEVEL "WARN" /* default TZ handling */ #define ESODBC_DEF_APPLY_TZ "no" +/* default early execution flag */ +#define ESODBC_DEF_EARLY_EXEC "yes" /* default of scientific floats printing */ #define ESODBC_DEF_SCI_FLOATS ESODBC_DSN_FLTS_DEF #define ESODBC_PWD_VAL_SUBST "" diff --git a/driver/dsn.c b/driver/dsn.c index 378976c9..55f003f4 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -76,6 +76,7 @@ int assign_dsn_attr(esodbc_dsn_attrs_st *attrs, {&MK_WSTR(ESODBC_DSN_MAX_FETCH_SIZE), &attrs->max_fetch_size}, {&MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB), &attrs->max_body_size}, {&MK_WSTR(ESODBC_DSN_APPLY_TZ), &attrs->apply_tz}, + {&MK_WSTR(ESODBC_DSN_EARLY_EXEC), &attrs->early_exec}, {&MK_WSTR(ESODBC_DSN_SCI_FLOATS), &attrs->sci_floats}, {&MK_WSTR(ESODBC_DSN_VERSION_CHECKING), &attrs->version_checking}, {&MK_WSTR(ESODBC_DSN_MFIELD_LENIENT), &attrs->mfield_lenient}, @@ -411,6 +412,7 @@ long TEST_API write_00_list(esodbc_dsn_attrs_st *attrs, {&MK_WSTR(ESODBC_DSN_MAX_FETCH_SIZE), &attrs->max_fetch_size}, {&MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB), &attrs->max_body_size}, {&MK_WSTR(ESODBC_DSN_APPLY_TZ), &attrs->apply_tz}, + {&MK_WSTR(ESODBC_DSN_EARLY_EXEC), &attrs->early_exec}, {&MK_WSTR(ESODBC_DSN_SCI_FLOATS), &attrs->sci_floats}, {&MK_WSTR(ESODBC_DSN_VERSION_CHECKING), &attrs->version_checking}, {&MK_WSTR(ESODBC_DSN_MFIELD_LENIENT), &attrs->mfield_lenient}, @@ -675,6 +677,10 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *new_attrs, &MK_WSTR(ESODBC_DSN_APPLY_TZ), &new_attrs->apply_tz, old_attrs ? &old_attrs->apply_tz : NULL }, + { + &MK_WSTR(ESODBC_DSN_EARLY_EXEC), &new_attrs->early_exec, + old_attrs ? &old_attrs->early_exec : NULL + }, { &MK_WSTR(ESODBC_DSN_SCI_FLOATS), &new_attrs->sci_floats, old_attrs ? &old_attrs->sci_floats : NULL @@ -786,6 +792,7 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, {&attrs->max_fetch_size, &MK_WSTR(ESODBC_DSN_MAX_FETCH_SIZE)}, {&attrs->max_body_size, &MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB)}, {&attrs->apply_tz, &MK_WSTR(ESODBC_DSN_APPLY_TZ)}, + {&attrs->early_exec, &MK_WSTR(ESODBC_DSN_EARLY_EXEC)}, {&attrs->sci_floats, &MK_WSTR(ESODBC_DSN_SCI_FLOATS)}, {&attrs->version_checking, &MK_WSTR(ESODBC_DSN_VERSION_CHECKING)}, {&attrs->mfield_lenient, &MK_WSTR(ESODBC_DSN_MFIELD_LENIENT)}, @@ -884,6 +891,9 @@ void assign_dsn_defaults(esodbc_dsn_attrs_st *attrs) res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_APPLY_TZ), &MK_WSTR(ESODBC_DEF_APPLY_TZ), /*overwrite?*/FALSE); + res |= assign_dsn_attr(attrs, + &MK_WSTR(ESODBC_DSN_EARLY_EXEC), &MK_WSTR(ESODBC_DEF_EARLY_EXEC), + /*overwrite?*/FALSE); res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_SCI_FLOATS), &MK_WSTR(ESODBC_DEF_SCI_FLOATS), /*overwrite?*/FALSE); diff --git a/driver/dsn.h b/driver/dsn.h index 25f17de2..cda5d508 100644 --- a/driver/dsn.h +++ b/driver/dsn.h @@ -35,6 +35,7 @@ #define ESODBC_DSN_MAX_FETCH_SIZE "MaxFetchSize" #define ESODBC_DSN_MAX_BODY_SIZE_MB "MaxBodySizeMB" #define ESODBC_DSN_APPLY_TZ "ApplyTZ" +#define ESODBC_DSN_EARLY_EXEC "EarlyExecution" #define ESODBC_DSN_SCI_FLOATS "ScientificFloats" #define ESODBC_DSN_VERSION_CHECKING "VersionChecking" #define ESODBC_DSN_MFIELD_LENIENT "MultiFieldLenient" @@ -78,6 +79,7 @@ typedef struct { wstr_st max_fetch_size; wstr_st max_body_size; wstr_st apply_tz; + wstr_st early_exec; wstr_st sci_floats; wstr_st version_checking; wstr_st mfield_lenient; @@ -86,7 +88,7 @@ typedef struct { wstr_st trace_enabled; wstr_st trace_file; wstr_st trace_level; -#define ESODBC_DSN_ATTRS_COUNT 28 +#define ESODBC_DSN_ATTRS_COUNT 29 SQLWCHAR buff[ESODBC_DSN_ATTRS_COUNT * ESODBC_DSN_MAX_ATTR_LEN]; /* DSN reading/writing functions are passed a SQLSMALLINT lenght param */ #if SHRT_MAX < ESODBC_DSN_ATTRS_COUNT * ESODBC_DSN_MAX_ATTR_LEN diff --git a/driver/handles.c b/driver/handles.c index abfba1bf..f79d8b3d 100644 --- a/driver/handles.c +++ b/driver/handles.c @@ -170,6 +170,7 @@ static void init_stmt(esodbc_stmt_st *stmt, SQLHANDLE InputHandle) * set at connection level. */ stmt->metadata_id = DBCH(InputHandle)->metadata_id; stmt->sql2c_conversion = CONVERSION_UNCHECKED; + stmt->early_executed = FALSE; } void dump_record(esodbc_rec_st *rec) diff --git a/driver/handles.h b/driver/handles.h index a4b8c3ed..c660437d 100644 --- a/driver/handles.h +++ b/driver/handles.h @@ -161,6 +161,7 @@ typedef struct struct_dbc { ESODBC_CMPSS_AUTO, } compression; BOOL apply_tz; /* should the times be converted from UTC to local TZ? */ + BOOL early_exec; /* should prepared, non-param queries be exec'd early? */ enum { ESODBC_FLTS_DEFAULT = 0, ESODBC_FLTS_SCIENTIFIC, @@ -391,6 +392,8 @@ typedef struct struct_stmt { CONVERSION_SUPPORTED, CONVERSION_SKIPPED, /* used with driver's meta queries */ } sql2c_conversion; + /* early execution */ + BOOL early_executed; /* SQLGetData state members */ SQLINTEGER gd_col; /* current column to get from, if positive */ diff --git a/driver/queries.c b/driver/queries.c index 3a1c682c..f8605079 100644 --- a/driver/queries.c +++ b/driver/queries.c @@ -39,6 +39,7 @@ /* fwd decl */ static SQLRETURN statement_params_len_cbor(esodbc_stmt_st *stmt, size_t *enc_len, size_t *conv_len); +static SQLRETURN count_param_markers(esodbc_stmt_st *stmt, SQLSMALLINT *p_cnt); static thread_local cstr_st tz_param; static cstr_st version = CSTR_INIT(STR(DRV_VERSION)); /* build-time define */ @@ -1103,6 +1104,7 @@ SQLRETURN TEST_API attach_sql(esodbc_stmt_st *stmt, /* if the app correctly SQL_CLOSE'es the statement, this would not be * needed. but just in case: re-init counter of total # of rows and sets */ STMT_ROW_CNT_RESET(stmt); + stmt->early_executed = false; return SQL_SUCCESS; } @@ -2407,6 +2409,7 @@ SQLRETURN EsSQLPrepareW { esodbc_stmt_st *stmt = STMH(hstmt); SQLRETURN ret; + SQLSMALLINT markers; if (cchSqlStr == SQL_NTS) { cchSqlStr = (SQLINTEGER)wcslen(szSqlStr); @@ -2420,7 +2423,29 @@ SQLRETURN EsSQLPrepareW ret = EsSQLFreeStmt(stmt, ESODBC_SQL_CLOSE); assert(SQL_SUCCEEDED(ret)); /* can't return error */ - return attach_sql(stmt, szSqlStr, cchSqlStr); + ret = attach_sql(stmt, szSqlStr, cchSqlStr); + /* if early execution mode is on and the statement has no parameter + * markers, execute the query right away */ + if (HDRH(stmt)->dbc->early_exec && SQL_SUCCEEDED(ret)) { + assert(! stmt->early_executed); /* cleared by now */ + if (! SQL_SUCCEEDED(count_param_markers(stmt, &markers))) { + ERRH(stmt, "failed to count parameter markers in query. " + "Early execution disabled."); + /* clear set diagnostic */ + init_diagnostic(&HDRH(stmt)->diag); + return ret; + } + if (0 < markers) { + INFOH(stmt, "query contains %hd parameter markers -- early " + "execution disabled.", markers); + return ret; + } + ret = EsSQLExecute(hstmt); + if (SQL_SUCCEEDED(ret)) { + stmt->early_executed = true; + } + } + return ret; } @@ -3604,7 +3629,18 @@ SQLRETURN EsSQLExecute(SQLHSTMT hstmt) char buff[ESODBC_BODY_BUF_START_SIZE]; cstr_st body = {buff, sizeof(buff)}; - DBGH(stmt, "executing SQL: [%zd] `" LCPDL "`.", stmt->u8sql.cnt, + if (stmt->early_executed) { + stmt->early_executed = false; /* re-enable subsequent executions */ + if (STMT_HAS_RESULTSET(stmt)) { + DBGH(stmt, "query early executed: [%zd] `" LCPDL "`.", + stmt->u8sql.cnt, LCSTR(&stmt->u8sql)); + return SQL_SUCCESS; + } else { + WARNH(stmt, "query `" LCPDL "` early executed, but lacking result" + " set.", LCSTR(&stmt->u8sql)); + } + } + DBGH(stmt, "executing query: [%zd] `" LCPDL "`.", stmt->u8sql.cnt, LCSTR(&stmt->u8sql)); ret = serialize_statement(stmt, &body);