driver/util.h (159 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ #ifndef __UTIL_H__ #define __UTIL_H__ /* NOTE: this must be included in "top level" file (wherever SQL types are * used */ #if defined(_WIN32) || defined (WIN32) #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> /* win function parameter attributes */ #include <windows.h> #include <tchar.h> #endif /* _WIN32/WIN32 */ #include <inttypes.h> #include <wchar.h> #include <assert.h> #include "sql.h" #include "sqlext.h" /* export attribute for internal functions used for testing */ #ifndef TEST_API /* Release builds define this to an empty macro */ #ifdef DRIVER_BUILD #define TEST_API __declspec(dllexport) #else /* DRIVER_BUILD */ #define TEST_API __declspec(dllimport) #endif /* DRIVER_BUILD */ #define TESTING /* compiles in the testing code */ #endif /* TEST_API */ /* * Assert two integral types have same storage and sign. */ #define ASSERT_INTEGER_TYPES_EQUAL(a, b) \ assert((sizeof(a) == sizeof(b)) && \ ( (0 < (a)0 - 1 && 0 < (b)0 - 1) || \ (0 > (a)0 - 1 && 0 > (b)0 - 1) )) /* * Stringifying in two preproc. passes */ #define _STR(_x) # _x #define STR(_x) _STR(_x) /* * Turn a C static string to wide char string. */ #define _MK_WPTR(_cstr_) (L ## _cstr_) #define MK_WPTR(_cstr_) _MK_WPTR(_cstr_) #define MK_CPTR(_cstr_) _cstr_ #ifdef UNICODE #define MK_TPTR MK_WPTR #else /* UNICODE */ #define MK_TPTR MK_CPTR #endif /* UNICODE */ #if !defined(_WIN32) && !defined (WIN32) #define _T MK_TPTR #endif /* _WIN32/WIN32 */ typedef struct cstr { SQLCHAR *str; size_t cnt; } cstr_st; /* * Converts a wchar_t string to a C string for ASCII characters. * 'dst' should be at least as character-long as 'src', if 'src' is * 0-terminated, OR one character longer otherwise (for the 0-term). * 'dst' will always be 0-term'd. * Returns negative if conversion fails, OR number of converted wchars, * including/plus the 0-term. */ int TEST_API ascii_w2c(const SQLWCHAR *src, SQLCHAR *dst, size_t chars); int TEST_API ascii_c2w(const SQLCHAR *src, SQLWCHAR *dst, size_t chars); /* * Compare two SQLWCHAR object, case INsensitive. */ int wmemncasecmp(const SQLWCHAR *a, const SQLWCHAR *b, size_t len); /* * Compare two zero-terminated SQLWCHAR* objects, until a 0 is encountered in * either of them or until 'count' characters are evaluated. If 'count' * parameter is negative, it is ignored. * * This is useful in comparing SQL strings which the API allows to be passed * either as 0-terminated or not (SQL_NTS). * The function does a single pass (no length evaluation of the strings). * wmemcmp() might read over the boundary of one of the objects, if the * provided 'count' paramter is not the minimum of the strings' length. */ int wszmemcmp(const SQLWCHAR *a, const SQLWCHAR *b, long count); /* * wcsstr() variant for non-NTS. */ const SQLWCHAR *wcsnstr(const SQLWCHAR *hay, size_t len, SQLWCHAR needle); typedef struct wstr { SQLWCHAR *str; size_t cnt; } wstr_st; /* * Turns a static C string into a wstr_st. */ #ifndef __cplusplus /* no MSVC support for compound literals with /TP */ # define MK_WSTR(_s) ((wstr_st){.str = MK_WPTR(_s), .cnt = sizeof(_s) - 1}) # define MK_CSTR(_s) ((cstr_st){.str = _s, .cnt = sizeof(_s) - 1}) #endif /* !__cplusplus */ #define WSTR_INIT(_s) {MK_WPTR(_s), sizeof(_s) - 1} #define CSTR_INIT(_s) {(SQLCHAR *)_s, sizeof(_s) - 1} /* * Test equality of two wstr_st objects. */ #define EQ_WSTR(s1, s2) \ ((s1)->cnt == (s2)->cnt && wmemcmp((s1)->str, (s2)->str, (s1)->cnt) == 0) /* * Same as EQ_WSTR, but case INsensitive. */ #define EQ_CASE_WSTR(s1, s2) \ ((s1)->cnt == (s2)->cnt && \ wmemncasecmp((s1)->str, (s2)->str, (s1)->cnt) == 0) typedef struct { BOOL wide; union { wstr_st w; cstr_st c; }; } xstr_st; /* For as long as wptr_st & cptr_st are only differing in string pointer type, * it's safe to save the check if wide or not. */ #define XSTR_LEN(_xptr) (_xptr)->w.cnt /* * Trims leading and trailing WS of a wide string of 'chars' length. * 0-terminator should not be counted (as it's a non-WS). */ void trim_ws(cstr_st *str); void wltrim_ws(wstr_st *wstr); void wrtrim_ws(wstr_st *wstr); #define wtrim_ws(_w) do { wltrim_ws(_w); wrtrim_ws(_w); } while (0) /* Trim the w-string at first encounter of the give w-char. * Returns TRUE if character has been encounter / trimming occured. */ BOOL wtrim_at(wstr_st *wstr, SQLWCHAR wchar); BOOL wstr2bool(wstr_st *val); /* Converts a [cw]str_st to a SQL(U)BIGINT. * If !strict, parsing stops at first non-digit char. * Returns the number of parsed characters or negative of failure. */ int str2ubigint(void *val, BOOL wide, SQLUBIGINT *out, BOOL strict); int str2bigint(void *val, BOOL wide, SQLBIGINT *out, BOOL strict); int str2double(void *val, BOOL wide, SQLDOUBLE *dbl, BOOL strict); /* converts the int types to a C or wide string, returning the string length */ size_t i64tot(int64_t i64, void *buff, BOOL wide); size_t ui64tot(uint64_t ui64, void *buff, BOOL wide); #ifdef _WIN32 /* * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/unicode : * "the only Unicode encoding that ODBC supports is UCS-2" (at Driver-DM * interface level) => the driver will convert between ES's UTF-8 multi-byte * and DM's UTF-16 wide char. */ /* * WideCharToMultiByte(): * "[D]oes not null-terminate an output string if the input string length is * explicitly specified without a terminating null character. To * null-terminate an output string for this function, the application should * pass in -1 or explicitly count the terminating null character for the input * string." * "If successful, returns the number of bytes written" or required (if * _ubytes == 0), OR "0 if it does not succeed". */ #define U16WC_TO_MBU8(_wstr, _wcnt, _u8str, _u8len) \ WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, \ _wstr, (int)(_wcnt), _u8str, (int)(_u8len), \ NULL, NULL) #define U8MB_TO_U16WC(_u8str, _u8len, _wstr, _wcnt) \ MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, \ _u8str, (int)(_u8len), _wstr, (int)(_wcnt)) #define WAPI_ERRNO() GetLastError() #define WAPI_CLR_ERRNO() SetLastError(ERROR_SUCCESS) #define WAPI_ERR_EBUFF() (GetLastError() == ERROR_INSUFFICIENT_BUFFER) /* * Locking type and primitives. */ typedef SRWLOCK esodbc_mutex_lt; #define ESODBC_MUX_SINIT SRWLOCK_INIT #define ESODBC_MUX_INIT(_m) InitializeSRWLock(_m) #define ESODBC_MUX_DEL(_m) /* not needed/possible */ #define ESODBC_MUX_LOCK(_m) AcquireSRWLockExclusive(_m) #define ESODBC_MUX_TRYLOCK(_m) TryAcquireSRWLockExclusive(_m) #define ESODBC_MUX_UNLOCK(_m) ReleaseSRWLockExclusive(_m) #if defined(DRIVER_BUILD) && !defined(thread_local) #define thread_local __declspec(thread) #endif /* DRIVER_BUILD && !thread_local */ #define timegm _mkgmtime #define strncasecmp _strnicmp #else /* _WIN32 */ #error "unsupported platform" /* "[R]eturns the number of bytes written into the multibyte output * string, excluding the terminating NULL (if any)". Copies until \0 is * met in wstr or buffer runs out. If \0 is met, it's copied, but not * counted in return. (silly fn) */ /* "[T]he multibyte character string at mbstr is null-terminated * only if wcstombs encounters a wide-character null character * during conversion." */ // wcstombs(charp, wstr, octet_length); #endif /* _WIN32 */ #ifdef UNICODE typedef wstr_st tstr_st; #else /* UNICODE */ typedef cstr_st tstr_st; #endif /* UNICODE */ /* generic char JSON escaping prefix */ #define JSON_ESC_GEN_PREF "\\u00" /* octet length of one generic JSON escaped character */ #define JSON_ESC_SEQ_SZ (sizeof(JSON_ESC_GEN_PREF) - 1 + /*0xAB*/2) /* * JSON-escapes a string. * If string len is 0, it assumes a NTS. * If output buffer (jout) is NULL, it returns the buffer size needed for * escaping. * Returns number of used bytes in buffer (which might be less than buffer * size, if some char needs an escaping longer than remaining space). */ size_t json_escape(const char *jin, size_t inlen, char *jout, size_t outlen); /* * JSON-escapes a string (str), outputting the result in the same buffer. * The buffer needs to be long enough (outlen) for this operation (at * least json_escaped_len() long). * If string [in]len is 0, it assumes a NTS. * Returns number of used bytes in buffer (which might be less than out buffer * size (outlen), if some char needs an escaping longer than remaining space). */ size_t json_escape_overlapping(char *str, size_t inlen, size_t outlen); /* * Copy a WSTR back to application. * The WSTR must not count the 0-tem. * The function checks against the correct size of available bytes, copies the * wstr according to avaialble space and indicates the available bytes to copy * back into provided buffer (if not NULL). */ SQLRETURN TEST_API write_wstr(SQLHANDLE hnd, SQLWCHAR *dest, wstr_st *src, SQLSMALLINT /*B*/avail, SQLSMALLINT /*B*/*usedp); /* * Converts a UTF-16 wide string to a UTF-8 MB, allocating the necessary space. * The \0 is allocated and written, even if not present in source string, but * only counted in output string if counted in input one. * If 'dst' is null, the destination is also going to be allocate (collated * with the string). The caller only needs to free the allocated chunk * (returned pointer or dst->str). * Returns NULL on error. */ cstr_st TEST_API *wstr_to_utf8(wstr_st *src, cstr_st *dst); /* the inverse of wstr_to_utf8(). */ wstr_st TEST_API *utf8_to_wstr(cstr_st *src, wstr_st *dst); /* Escape `%`, `_`, `\` characters in 'src'. * If not 'force'-d, the escaping will stop on detection of pre-existing * escaping(*), OR if the chars to be escaped are stand-alone. * (*): invalid/incomplete escaping sequences - `\\\` - are still considered * as containing escaping. * Returns: TRUE, if escaping has been applied */ BOOL TEST_API metadata_id_escape(wstr_st *src, wstr_st *dst, BOOL force); /* Simple hex printing of a cstr_st object. * Returns (thread local static) printed buffer, always 0-term'd. */ char *cstr_hex_dump(const cstr_st *buff); /* * Printing aids. */ /* * w/printf() desriptors for char/wchar_t * * "WPrintF Wide/Char Pointer _ DESCriptor" */ #ifdef _WIN32 /* wprintf wide_t pointer descriptor */ # define WPFWP_DESC L"%s" # define WPFWP_LDESC L"%.*s" /* printf wide_t pointer descriptor */ # define PFWP_DESC "%S" # define PFWP_LDESC "%.*S" /* wprintf char pointer descriptor */ # define WPFCP_DESC L"%S" # define WPFCP_LDESC L"%.*S" /* printf char pointer descriptor */ # define PFCP_DESC "%s" # define PFCP_LDESC "%.*s" #else /* _WIN32 */ /* wprintf wide_t pointer descriptor */ # define WPFWP_DESC L"%S" # define WPFWP_LDESC L"%.*S" /* printf wide_t pointer descriptor */ # define PFWP_DESC "%S" # define PFWP_LDESC "%.*S" /* wprintf char pointer descriptor */ # define WPFCP_DESC L"%s" # define WPFCP_LDESC L"%.*s" /* printf char pointer descriptor */ # define PFCP_DESC "%s" # define PFCP_LDESC "%.*s" #endif /* _WIN32 */ /* ISO time formats lengths. * ES/SQL interface should only use UTC ('Z'ulu offset). */ #define ISO8601_TIMESTAMP_LEN(prec) \ (sizeof("yyyy-mm-ddThh:mm:ss+hh:mm") - /*\0*/1 + /*'.'*/!!prec + prec) #define ISO8601_TS_UTC_LEN(prec) \ (sizeof("yyyy-mm-ddThh:mm:ssZ") - /*\0*/1 + /*'.'*/!!prec + prec) #define ISO8601_TIMESTAMP_MAX_LEN \ ISO8601_TIMESTAMP_LEN(ESODBC_MAX_SEC_PRECISION) #define ISO8601_TIMESTAMP_MIN_LEN \ ISO8601_TS_UTC_LEN(0) #define DATE_TEMPLATE_LEN \ (sizeof("yyyy-mm-dd") - /*\0*/1) #define TIME_TEMPLATE_LEN(prec) \ (sizeof("hh:mm:ss") - /*\0*/1 + /*'.'*/!!prec + prec) #define TIMESTAMP_TEMPLATE_LEN(prec) \ (DATE_TEMPLATE_LEN + /*' '*/1 + TIME_TEMPLATE_LEN(prec)) #define TIMESTAMP_NOSEC_TEMPLATE_LEN \ (DATE_TEMPLATE_LEN + /*' '*/1 + sizeof("hh:mm") - /*\0*/1) #endif /* __UTIL_H__ */ /* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */