📄 query.c
字号:
tds_skip_comment_ucs2le(const char *s, const char *end){ const char *p = s; if (p+4 <= end && memcmp(p, "-\0-", 4) == 0) { for (;(p+=2) < end;) if (p[0] == '\n' && p[1] == 0) return p + 2; } else if (p+4 <= end && memcmp(p, "/\0*", 4) == 0) { p += 2; end -= 2; for(;(p+=2) < end;) if (memcmp(p, "*\0/", 4) == 0) return p + 4; } else p += 2; return p;}static const char *tds_skip_quoted_ucs2le(const char *s, const char *end){ const char *p = s; char quote = (*s == '[') ? ']' : *s; assert(s[1] == 0 && s < end && (end - s) % 2 == 0); for (; (p += 2) != end;) { if (p[0] == quote && !p[1]) { p += 2; if (p == end || p[0] != quote || p[1]) return p; } } return p;}static const char *tds_next_placeholder_ucs2le(const char *start, const char *end, int named){ const char *p = start; char prev = ' ', c; assert(p && start <= end && (end - start) % 2 == 0); for (; p != end;) { if (p[1]) { prev = ' '; p += 2; continue; } c = p[0]; switch (c) { case '\'': case '\"': case '[': p = tds_skip_quoted_ucs2le(p, end); break; case '-': case '/': p = tds_skip_comment_ucs2le(p, end); c = ' '; break; case '?': return p; case '@': if (named && !isalnum((unsigned char) prev)) return p; default: p += 2; break; } prev = c; } return end;}static inttds_count_placeholders_ucs2le(const char *query, const char *query_end){ const char *p = query - 2; int count = 0; for (;; ++count) { if ((p = tds_next_placeholder_ucs2le(p + 2, query_end, 0)) == query_end) return count; }}/** * Return declaration for column (like "varchar(20)") * \param tds state information for the socket and the TDS protocol * \param curcol column * \param out buffer to hold declaration * \return TDS_FAIL or TDS_SUCCEED */static inttds_get_column_declaration(TDSSOCKET * tds, TDSCOLUMN * curcol, char *out){ const char *fmt = NULL; int max_len = IS_TDS7_PLUS(tds) ? 8000 : 255; CHECK_TDS_EXTRA(tds); CHECK_COLUMN_EXTRA(curcol); switch (tds_get_conversion_type(curcol->on_server.column_type, curcol->on_server.column_size)) { case XSYBCHAR: case SYBCHAR: fmt = "CHAR(%d)"; break; case SYBVARCHAR: case XSYBVARCHAR: fmt = "VARCHAR(%d)"; break; case SYBINT1: fmt = "TINYINT"; break; case SYBINT2: fmt = "SMALLINT"; break; case SYBINT4: fmt = "INT"; break; case SYBINT8: /* TODO even for Sybase ?? */ fmt = "BIGINT"; break; case SYBFLT8: fmt = "FLOAT"; break; case SYBDATETIME: fmt = "DATETIME"; break; case SYBBIT: fmt = "BIT"; break; case SYBTEXT: fmt = "TEXT"; break; case SYBLONGBINARY: /* TODO correct ?? */ case SYBIMAGE: fmt = "IMAGE"; break; case SYBMONEY4: fmt = "SMALLMONEY"; break; case SYBMONEY: fmt = "MONEY"; break; case SYBDATETIME4: fmt = "SMALLDATETIME"; break; case SYBREAL: fmt = "REAL"; break; case SYBBINARY: case XSYBBINARY: fmt = "BINARY(%d)"; break; case SYBVARBINARY: case XSYBVARBINARY: fmt = "VARBINARY(%d)"; break; case SYBNUMERIC: fmt = "NUMERIC(%d,%d)"; goto numeric_decimal; case SYBDECIMAL: fmt = "DECIMAL(%d,%d)"; numeric_decimal: sprintf(out, fmt, curcol->column_prec, curcol->column_scale); return TDS_SUCCEED; break; case SYBUNIQUE: if (IS_TDS7_PLUS(tds)) fmt = "UNIQUEIDENTIFIER"; break; case SYBNTEXT: if (IS_TDS7_PLUS(tds)) fmt = "NTEXT"; break; case SYBNVARCHAR: case XSYBNVARCHAR: if (IS_TDS7_PLUS(tds)) { fmt = "NVARCHAR(%d)"; max_len = 4000; } break; case XSYBNCHAR: if (IS_TDS7_PLUS(tds)) { fmt = "NCHAR(%d)"; max_len = 4000; } break; /* nullable types should not occur here... */ case SYBFLTN: case SYBMONEYN: case SYBDATETIMN: case SYBBITN: case SYBINTN: assert(0); /* TODO... */ case SYBVOID: case SYBSINT1: case SYBUINT2: case SYBUINT4: case SYBUINT8: case SYBVARIANT: break; } if (fmt) { TDS_INT size = curcol->on_server.column_size; if (!size) size = curcol->column_size; /* fill out */ sprintf(out, fmt, size > 0 ? (size > max_len ? max_len : size) : 1); return TDS_SUCCEED; } out[0] = 0; return TDS_FAIL;}/** * Return string with parameters definition, useful for TDS7+ * \param tds state information for the socket and the TDS protocol * \param params parameters to build declaration * \param out_len length output buffer in bytes * \return allocated and filled string or NULL on failure (coded in ucs2le charset ) *//* TODO find a better name for this function */static char *tds7_build_param_def_from_query(TDSSOCKET * tds, const char* converted_query, int converted_query_len, TDSPARAMINFO * params, size_t *out_len){ size_t size = 512; char *param_str; char *p; char declaration[40]; size_t l = 0; int i, count; assert(IS_TDS7_PLUS(tds)); assert(out_len); CHECK_TDS_EXTRA(tds); if (params) CHECK_PARAMINFO_EXTRA(params); count = tds_count_placeholders_ucs2le(converted_query, converted_query + converted_query_len); param_str = (char *) malloc(512); if (!param_str) return NULL; for (i = 0; i < count; ++i) { if (l > 0u) { param_str[l++] = ','; param_str[l++] = 0; } /* realloc on insufficient space */ while ((l + (2u * 40u)) > size) { p = (char *) realloc(param_str, size += 512u); if (!p) goto Cleanup; param_str = p; } /* get this parameter declaration */ sprintf(declaration, "@P%d ", i+1); if (params && i < params->num_cols) { if (tds_get_column_declaration(tds, params->columns[i], declaration + strlen(declaration)) == TDS_FAIL) goto Cleanup; } else { strcat(declaration, "varchar(80)"); } /* convert it to ucs2 and append */ l += tds_ascii_to_ucs2(param_str + l, declaration); } *out_len = l; return param_str; Cleanup: free(param_str); return NULL;}/** * Return string with parameters definition, useful for TDS7+ * \param tds state information for the socket and the TDS protocol * \param params parameters to build declaration * \param out_len length output buffer in bytes * \return allocated and filled string or NULL on failure (coded in ucs2le charset ) *//* TODO find a better name for this function */static char *tds7_build_param_def_from_params(TDSSOCKET * tds, const char* query, size_t query_len, TDSPARAMINFO * params, size_t *out_len){ size_t size = 512; char *param_str; char *p; char declaration[40]; size_t l = 0; int i; struct tds_ids { const char *p; size_t len; } *ids = NULL; assert(IS_TDS7_PLUS(tds)); assert(out_len); CHECK_TDS_EXTRA(tds); if (params) CHECK_PARAMINFO_EXTRA(params); param_str = (char *) malloc(512); if (!param_str) return NULL; /* try to detect missing names */ if (params->num_cols) { ids = (struct tds_ids *) calloc(params->num_cols, sizeof(struct tds_ids)); if (!ids) goto Cleanup; if (!params->columns[0]->column_name[0]) { const char *s = query, *e, *id_end; const char *query_end = query + query_len; for (i = 0; i < params->num_cols; s = e + 2) { e = tds_next_placeholder_ucs2le(s, query_end, 1); if (e == query_end) break; if (e[0] != '@') continue; /* find end of param name */ for (id_end = e + 2; id_end != query_end; id_end += 2) if (!id_end[1] && (id_end[0] != '_' && id_end[1] != '#' && !isalnum((unsigned char) id_end[0]))) break; ids[i].p = e; ids[i].len = id_end - e; ++i; } } } for (i = 0; i < params->num_cols; ++i) { const char *ib; char *ob; size_t il, ol; if (l > 0u) { param_str[l++] = ','; param_str[l++] = 0; } /* realloc on insufficient space */ il = ids[i].p ? ids[i].len : 2 * params->columns[i]->column_namelen; while ((l + (2u * 26u) + il) > size) { p = (char *) realloc(param_str, size += 512); if (!p) goto Cleanup; param_str = p; } /* this part of buffer can be not-ascii compatible, use all ucs2... */ if (ids[i].len) { memcpy(param_str + l, ids[i].p, ids[i].len); l += ids[i].len; } else { ib = params->columns[i]->column_name; il = params->columns[i]->column_namelen; ob = param_str + l; ol = size - l; memset(&tds->char_convs[iso2server_metadata]->suppress, 0, sizeof(tds->char_convs[iso2server_metadata]->suppress)); if (tds_iconv(tds, tds->char_convs[iso2server_metadata], to_server, &ib, &il, &ob, &ol) == (size_t) - 1) goto Cleanup; l = size - ol; } param_str[l++] = ' '; param_str[l++] = 0; /* get this parameter declaration */ tds_get_column_declaration(tds, params->columns[i], declaration); if (!declaration[0]) goto Cleanup; /* convert it to ucs2 and append */ l += tds_ascii_to_ucs2(param_str + l, declaration); } free(ids); *out_len = l; return param_str; Cleanup: free(ids); free(param_str); return NULL;}/** * Output params types and query (required by sp_prepare/sp_executesql/sp_prepexec) * \param tds state information for the socket and the TDS protocol * \param query query (in ucs2le codings) * \param query_len query length in bytes */static voidtds7_put_query_params(TDSSOCKET * tds, const char *query, int query_len){ int len, i, num_placeholders; const char *s, *e; char buf[24]; const char *const query_end = query + query_len; CHECK_TDS_EXTRA(tds); assert(IS_TDS7_PLUS(tds)); /* we use all "@PX" for parameters */ num_placeholders = tds_count_placeholders_ucs2le(query, query_end); len = num_placeholders * 2; /* adjust for the length of X */ for (i = 10; i <= num_placeholders; i *= 10) { len += num_placeholders - i + 1; } /* string with sql statement */ /* replace placeholders with dummy parametes */ tds_put_byte(tds, 0); tds_put_byte(tds, 0); tds_put_byte(tds, SYBNTEXT); /* must be Ntype */ len = 2 * len + query_len; tds_put_int(tds, len); if (IS_TDS8_PLUS(tds)) tds_put_n(tds, tds->collation, 5); tds_put_int(tds, len); s = query; /* TODO do a test with "...?" and "...?)" */ for (i = 1;; ++i) { e = tds_next_placeholder_ucs2le(s, query_end, 0); assert(e && query <= e && e <= query_end); tds_put_n(tds, s, e - s); if (e == query_end) break; sprintf(buf, "@P%d", i); tds_put_string(tds, buf, -1); s = e + 2; }}static voidtds7_put_params_definition(TDSSOCKET * tds, const char *param_definition, size_t param_length){ CHECK_TDS_EXTRA(tds); /* string with parameters types */ tds_put_byte(tds, 0); tds_put_byte(tds, 0); tds_put_byte(tds, SYBNTEXT); /* must be Ntype */ /* put parameters definitions */ tds_put_int(tds, param_length); if (IS_TDS8_PLUS(tds)) tds_put_n(tds, tds->collation, 5); tds_put_int(tds, param_length ? param_length : -1); tds_put_n(tds, param_definition, param_length);}/** * tds_submit_prepare() creates a temporary stored procedure in the server. * Under TDS 4.2 dynamic statements are emulated building sql command * \param tds state information for the socket and the TDS protocol * \param query language query with given placeholders (?) * \param id string to identify the dynamic query. Pass NULL for automatic generation. * \param dyn_out will receive allocated TDSDYNAMIC*. Any older allocated dynamic won't be freed, Can be NULL. * \param params parameters to use. It can be NULL even if parameters are present. Used only for TDS7+ * \return TDS_FAIL or TDS_SUCCEED *//* TODO parse all results ?? */inttds_submit_prepare(TDSSOCKET * tds, const char *query, const char *id, TDSDYNAMIC ** dyn_out, TDSPARAMINFO * params){ int id_len, query_len; int rc; TDSDYNAMIC *dyn; CHECK_TDS_EXTRA(tds); if (params) CHECK_PARAMINFO_EXTRA(params); if (!query) return TDS_FAIL; /* allocate a structure for this thing */ if (!id) { char *tmp_id = NULL; if (tds_get_dynid(tds, &tmp_id) == TDS_FAIL) return TDS_FAIL; dyn = tds_alloc_dynamic(tds, tmp_id); free(tmp_id); } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -