/Firebird-2.5.1.26351-0/src/dsql/dsql.cpp

# · C++ · 3406 lines · 2202 code · 500 blank · 704 comment · 439 complexity · 2a4aff76d17df2fb617ef6efae2dc1ca MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * PROGRAM: Dynamic SQL runtime support
  3. * MODULE: dsql.cpp
  4. * DESCRIPTION: Local processing for External entry points.
  5. *
  6. * The contents of this file are subject to the Interbase Public
  7. * License Version 1.0 (the "License"); you may not use this file
  8. * except in compliance with the License. You may obtain a copy
  9. * of the License at http://www.Inprise.com/IPL.html
  10. *
  11. * Software distributed under the License is distributed on an
  12. * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
  13. * or implied. See the License for the specific language governing
  14. * rights and limitations under the License.
  15. *
  16. * The Original Code was created by Inprise Corporation
  17. * and its predecessors. Portions created by Inprise Corporation are
  18. * Copyright (C) Inprise Corporation.
  19. *
  20. * All Rights Reserved.
  21. * Contributor(s): ______________________________________.
  22. * 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
  23. * conditionals, as the engine now fully supports
  24. * readonly databases.
  25. * December 2001 Mike Nordell: Major overhaul to (try to) make it C++
  26. * 2001.6.3 Claudio Valderrama: fixed a bad behaved loop in get_plan_info()
  27. * and get_rsb_item() that caused a crash when plan info was requested.
  28. * 2001.6.9 Claudio Valderrama: Added nod_del_view, nod_current_role and nod_breakleave.
  29. * 2002.10.29 Nickolay Samofatov: Added support for savepoints
  30. * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
  31. * 2004.01.16 Vlad Horsun: added support for EXECUTE BLOCK statement
  32. * Adriano dos Santos Fernandes
  33. */
  34. #include "firebird.h"
  35. #include <stdio.h>
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include "../dsql/dsql.h"
  39. #include "../dsql/node.h"
  40. #include "../jrd/ibase.h"
  41. #include "../jrd/align.h"
  42. #include "../jrd/intl.h"
  43. #include "../jrd/jrd.h"
  44. #include "../dsql/Parser.h"
  45. #include "../dsql/ddl_proto.h"
  46. #include "../dsql/dsql_proto.h"
  47. #include "../dsql/errd_proto.h"
  48. #include "../dsql/gen_proto.h"
  49. #include "../dsql/hsh_proto.h"
  50. #include "../dsql/make_proto.h"
  51. #include "../dsql/movd_proto.h"
  52. #include "../dsql/parse_proto.h"
  53. #include "../dsql/pass1_proto.h"
  54. #include "../jrd/blb_proto.h"
  55. #include "../jrd/cmp_proto.h"
  56. #include "../jrd/gds_proto.h"
  57. #include "../jrd/inf_proto.h"
  58. #include "../jrd/jrd_proto.h"
  59. #include "../jrd/tra_proto.h"
  60. #include "../jrd/trace/TraceManager.h"
  61. #include "../jrd/trace/TraceDSQLHelpers.h"
  62. #include "../common/classes/init.h"
  63. #include "../common/utils_proto.h"
  64. #ifdef SCROLLABLE_CURSORS
  65. #include "../jrd/scroll_cursors.h"
  66. #endif
  67. #include "../common/StatusArg.h"
  68. #ifdef HAVE_CTYPE_H
  69. #include <ctype.h>
  70. #endif
  71. using namespace Jrd;
  72. using namespace Dsql;
  73. using namespace Firebird;
  74. static void close_cursor(thread_db*, dsql_req*);
  75. static USHORT convert(SLONG, UCHAR*);
  76. static void execute_blob(thread_db*, dsql_req*, USHORT, const UCHAR*, USHORT, const UCHAR*,
  77. USHORT, UCHAR*, USHORT, UCHAR*);
  78. static void execute_immediate(thread_db*, Attachment*, jrd_tra**,
  79. USHORT, const TEXT*, USHORT,
  80. USHORT, const UCHAR*, /*USHORT,*/ USHORT, const UCHAR*,
  81. USHORT, UCHAR*, /*USHORT,*/ USHORT, UCHAR*);
  82. static void execute_request(thread_db*, dsql_req*, jrd_tra**, USHORT, const UCHAR*,
  83. USHORT, const UCHAR*, USHORT, UCHAR*, USHORT, UCHAR*, bool);
  84. static SSHORT filter_sub_type(const dsql_nod*);
  85. static bool get_indices(SLONG*, const UCHAR**, SLONG*, SCHAR**);
  86. static USHORT get_request_info(thread_db*, dsql_req*, SLONG, UCHAR*);
  87. static bool get_rsb_item(SLONG*, const UCHAR**, SLONG*, SCHAR**, USHORT*, USHORT*);
  88. static dsql_dbb* init(Attachment*);
  89. static void map_in_out(dsql_req*, dsql_msg*, USHORT, const UCHAR*, USHORT, UCHAR*, const UCHAR* = 0);
  90. static USHORT parse_blr(USHORT, const UCHAR*, const USHORT, dsql_par*);
  91. static dsql_req* prepare(thread_db*, dsql_dbb*, jrd_tra*, USHORT, const TEXT*, USHORT, USHORT);
  92. static UCHAR* put_item(UCHAR, const USHORT, const UCHAR*, UCHAR*, const UCHAR* const, const bool copy = true);
  93. static void release_request(thread_db*, dsql_req*, bool);
  94. static void sql_info(thread_db*, dsql_req*, USHORT, const UCHAR*, ULONG, UCHAR*);
  95. static UCHAR* var_info(dsql_msg*, const UCHAR*, const UCHAR* const, UCHAR*,
  96. const UCHAR* const, USHORT, bool);
  97. static inline bool reqTypeWithCursor(REQ_TYPE req_type)
  98. {
  99. switch (req_type)
  100. {
  101. case REQ_SELECT:
  102. case REQ_SELECT_BLOCK:
  103. case REQ_SELECT_UPD:
  104. case REQ_EMBED_SELECT:
  105. case REQ_GET_SEGMENT:
  106. case REQ_PUT_SEGMENT:
  107. return true;
  108. }
  109. return false;
  110. }
  111. #ifdef DSQL_DEBUG
  112. unsigned DSQL_debug = 0;
  113. #endif
  114. namespace
  115. {
  116. const UCHAR db_hdr_info_items[] =
  117. {
  118. isc_info_db_sql_dialect,
  119. isc_info_ods_version,
  120. isc_info_ods_minor_version,
  121. #ifdef SCROLLABLE_CURSORS
  122. isc_info_base_level,
  123. #endif
  124. isc_info_db_read_only,
  125. isc_info_end
  126. };
  127. const UCHAR explain_info[] =
  128. {
  129. isc_info_access_path
  130. };
  131. const UCHAR record_info[] =
  132. {
  133. isc_info_req_update_count, isc_info_req_delete_count,
  134. isc_info_req_select_count, isc_info_req_insert_count
  135. };
  136. const UCHAR sql_records_info[] =
  137. {
  138. isc_info_sql_records
  139. };
  140. } // namespace
  141. #ifdef DSQL_DEBUG
  142. IMPLEMENT_TRACE_ROUTINE(dsql_trace, "DSQL")
  143. #endif
  144. dsql_dbb::~dsql_dbb()
  145. {
  146. thread_db* tdbb = JRD_get_thread_data();
  147. while (!dbb_requests.isEmpty())
  148. release_request(tdbb, dbb_requests[0], true);
  149. HSHD_finish(this);
  150. }
  151. /**
  152. DSQL_allocate_statement
  153. @brief Allocate a statement handle.
  154. @param tdbb
  155. @param attachment
  156. **/
  157. dsql_req* DSQL_allocate_statement(thread_db* tdbb, Attachment* attachment)
  158. {
  159. SET_TDBB(tdbb);
  160. dsql_dbb* const database = init(attachment);
  161. Jrd::ContextPoolHolder context(tdbb, database->createPool());
  162. // allocate the request block
  163. MemoryPool& pool = *tdbb->getDefaultPool();
  164. dsql_req* const request = FB_NEW(pool) CompiledStatement(pool);
  165. request->req_dbb = database;
  166. database->dbb_requests.add(request);
  167. return request;
  168. }
  169. /**
  170. DSQL_execute
  171. @brief Execute a non-SELECT dynamic SQL statement.
  172. @param tdbb
  173. @param tra_handle
  174. @param request
  175. @param in_blr_length
  176. @param in_blr
  177. @param in_msg_type
  178. @param in_msg_length
  179. @param in_msg
  180. @param out_blr_length
  181. @param out_blr
  182. @param out_msg_type OBSOLETE
  183. @param out_msg_length
  184. @param out_msg
  185. **/
  186. void DSQL_execute(thread_db* tdbb,
  187. jrd_tra** tra_handle,
  188. dsql_req* request,
  189. USHORT in_blr_length, const UCHAR* in_blr,
  190. USHORT in_msg_type, USHORT in_msg_length, const UCHAR* in_msg,
  191. USHORT out_blr_length, UCHAR* out_blr,
  192. /*USHORT out_msg_type,*/ USHORT out_msg_length, UCHAR* out_msg)
  193. {
  194. SET_TDBB(tdbb);
  195. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  196. if (request->req_flags & REQ_orphan)
  197. {
  198. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
  199. Arg::Gds(isc_bad_req_handle));
  200. }
  201. if ((SSHORT) in_msg_type == -1) {
  202. request->req_type = REQ_EMBED_SELECT;
  203. }
  204. // Only allow NULL trans_handle if we're starting a transaction
  205. if (!*tra_handle && request->req_type != REQ_START_TRANS)
  206. {
  207. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
  208. Arg::Gds(isc_bad_trans_handle));
  209. }
  210. // If the request is a SELECT or blob statement then this is an open.
  211. // Make sure the cursor is not already open.
  212. if (reqTypeWithCursor(request->req_type)) {
  213. if (request->req_flags & REQ_cursor_open)
  214. {
  215. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
  216. Arg::Gds(isc_dsql_cursor_open_err));
  217. }
  218. }
  219. // A select with a non zero output length is a singleton select
  220. const bool singleton = (request->req_type == REQ_SELECT && out_msg_length != 0);
  221. if (request->req_type != REQ_EMBED_SELECT)
  222. {
  223. execute_request(tdbb, request, tra_handle,
  224. in_blr_length, in_blr, in_msg_length, in_msg,
  225. out_blr_length, out_blr, out_msg_length, out_msg,
  226. singleton);
  227. }
  228. else
  229. {
  230. request->req_transaction = *tra_handle;
  231. }
  232. // If the output message length is zero on a REQ_SELECT then we must
  233. // be doing an OPEN cursor operation.
  234. // If we do have an output message length, then we're doing
  235. // a singleton SELECT. In that event, we don't add the cursor
  236. // to the list of open cursors (it's not really open).
  237. if (reqTypeWithCursor(request->req_type) && !singleton)
  238. {
  239. request->req_flags |= REQ_cursor_open;
  240. TRA_link_cursor(request->req_transaction, request);
  241. }
  242. }
  243. /**
  244. DSQL_execute_immediate
  245. @brief Execute a non-SELECT dynamic SQL statement.
  246. @param tdbb
  247. @param attachment
  248. @param tra_handle
  249. @param length
  250. @param string
  251. @param dialect
  252. @param in_blr_length
  253. @param in_blr
  254. @param in_msg_type OBSOLETE
  255. @param in_msg_length
  256. @param in_msg
  257. @param out_blr_length
  258. @param out_blr
  259. @param out_msg_type OBSOLETE
  260. @param out_msg_length
  261. @param out_msg
  262. **/
  263. void DSQL_execute_immediate(thread_db* tdbb,
  264. Attachment* attachment,
  265. jrd_tra** tra_handle,
  266. USHORT length, const TEXT* string, USHORT dialect,
  267. USHORT in_blr_length, const UCHAR* in_blr,
  268. /*USHORT in_msg_type,*/ USHORT in_msg_length, const UCHAR* in_msg,
  269. USHORT out_blr_length, UCHAR* out_blr,
  270. /*USHORT out_msg_type,*/ USHORT out_msg_length, UCHAR* out_msg)
  271. {
  272. execute_immediate(tdbb, attachment, tra_handle, length,
  273. string, dialect,
  274. in_blr_length, in_blr, /*in_msg_type,*/ in_msg_length, in_msg,
  275. out_blr_length, out_blr, /*out_msg_type,*/ out_msg_length, out_msg);
  276. }
  277. /**
  278. DSQL_fetch
  279. @brief Fetch next record from a dynamic SQL cursor
  280. @param user_status
  281. @param req_handle
  282. @param blr_length
  283. @param blr
  284. @param msg_type OBSOLETE
  285. @param msg_length
  286. @param dsql_msg
  287. @param direction
  288. @param offset
  289. **/
  290. ISC_STATUS DSQL_fetch(thread_db* tdbb,
  291. dsql_req* request,
  292. USHORT blr_length, const UCHAR* blr,
  293. /*USHORT msg_type,*/ USHORT msg_length, UCHAR* dsql_msg_buf
  294. #ifdef SCROLLABLE_CURSORS
  295. , USHORT direction, SLONG offset
  296. #endif
  297. )
  298. {
  299. SET_TDBB(tdbb);
  300. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  301. // if the cursor isn't open, we've got a problem
  302. if (reqTypeWithCursor(request->req_type))
  303. {
  304. if (!(request->req_flags & REQ_cursor_open))
  305. {
  306. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
  307. Arg::Gds(isc_dsql_cursor_err) <<
  308. Arg::Gds(isc_dsql_cursor_not_open));
  309. }
  310. }
  311. #ifdef SCROLLABLE_CURSORS
  312. // check whether we need to send an asynchronous scrolling message
  313. // to the engine; the engine will automatically advance one record
  314. // in the same direction as before, so optimize out messages of that
  315. // type
  316. if (request->req_type == REQ_SELECT && request->req_dbb->dbb_base_level >= 5)
  317. {
  318. switch (direction)
  319. {
  320. case isc_fetch_next:
  321. if (!(request->req_flags & REQ_backwards))
  322. offset = 0;
  323. else {
  324. direction = blr_forward;
  325. offset = 1;
  326. request->req_flags &= ~REQ_backwards;
  327. }
  328. break;
  329. case isc_fetch_prior:
  330. if (request->req_flags & REQ_backwards)
  331. offset = 0;
  332. else {
  333. direction = blr_backward;
  334. offset = 1;
  335. request->req_flags |= REQ_backwards;
  336. }
  337. break;
  338. case isc_fetch_first:
  339. direction = blr_bof_forward;
  340. offset = 1;
  341. request->req_flags &= ~REQ_backwards;
  342. break;
  343. case isc_fetch_last:
  344. direction = blr_eof_backward;
  345. offset = 1;
  346. request->req_flags |= REQ_backwards;
  347. break;
  348. case isc_fetch_absolute:
  349. direction = blr_bof_forward;
  350. request->req_flags &= ~REQ_backwards;
  351. break;
  352. case isc_fetch_relative:
  353. if (offset < 0) {
  354. direction = blr_backward;
  355. offset = -offset;
  356. request->req_flags |= REQ_backwards;
  357. }
  358. else {
  359. direction = blr_forward;
  360. request->req_flags &= ~REQ_backwards;
  361. }
  362. break;
  363. default:
  364. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
  365. Arg::Gds(isc_dsql_sqlda_err));
  366. }
  367. if (offset)
  368. {
  369. DSC desc;
  370. dsql_msg* message = (dsql_msg*) request->req_async;
  371. desc.dsc_dtype = dtype_short;
  372. desc.dsc_scale = 0;
  373. desc.dsc_length = sizeof(USHORT);
  374. desc.dsc_flags = 0;
  375. desc.dsc_address = (UCHAR*) & direction;
  376. dsql_par* offset_parameter = message->msg_parameters;
  377. dsql_par* parameter = offset_parameter->par_next;
  378. MOVD_move(tdbb, &desc, &parameter->par_desc);
  379. desc.dsc_dtype = dtype_long;
  380. desc.dsc_scale = 0;
  381. desc.dsc_length = sizeof(SLONG);
  382. desc.dsc_flags = 0;
  383. desc.dsc_address = (UCHAR*) & offset;
  384. MOVD_move(tdbb, &desc, &offset_parameter->par_desc);
  385. DsqlCheckout dcoHolder(request->req_dbb);
  386. if (isc_receive2(tdbb->tdbb_status_vector, &request->req_request,
  387. message->msg_number, message->msg_length,
  388. message->msg_buffer, 0, direction, offset))
  389. {
  390. Firebird::status_exception::raise(tdbb->tdbb_status_vector);
  391. }
  392. }
  393. }
  394. #endif
  395. dsql_msg* message = (dsql_msg*) request->req_receive;
  396. // Set up things for tracing this call
  397. Attachment* att = request->req_dbb->dbb_attachment;
  398. TraceDSQLFetch trace(att, request);
  399. // Insure that the blr for the message is parsed, regardless of
  400. // whether anything is found by the call to receive.
  401. if (blr_length) {
  402. parse_blr(blr_length, blr, msg_length, message->msg_parameters);
  403. }
  404. if (request->req_type == REQ_GET_SEGMENT)
  405. {
  406. // For get segment, use the user buffer and indicator directly.
  407. dsql_par* parameter = request->req_blob->blb_segment;
  408. dsql_par* null = parameter->par_null;
  409. USHORT* ret_length = (USHORT *) (dsql_msg_buf + (IPTR) null->par_user_desc.dsc_address);
  410. UCHAR* buffer = dsql_msg_buf + (IPTR) parameter->par_user_desc.dsc_address;
  411. *ret_length = BLB_get_segment(tdbb, request->req_blob->blb_blob,
  412. buffer, parameter->par_user_desc.dsc_length);
  413. if (request->req_blob->blb_blob->blb_flags & BLB_eof)
  414. return 100;
  415. if (request->req_blob->blb_blob->blb_fragment_size)
  416. return 101;
  417. return 0;
  418. }
  419. JRD_receive(tdbb, request->req_request, message->msg_number, message->msg_length,
  420. message->msg_buffer, 0);
  421. const dsql_par* const eof = request->req_eof;
  422. const bool eof_reached = eof && !*((USHORT*) eof->par_desc.dsc_address);
  423. if (eof_reached)
  424. {
  425. trace.fetch(true, res_successful);
  426. return 100;
  427. }
  428. map_in_out(NULL, message, 0, blr, msg_length, dsql_msg_buf);
  429. trace.fetch(false, res_successful);
  430. return FB_SUCCESS;
  431. }
  432. /**
  433. DSQL_free_statement
  434. @brief Release request for a dsql statement
  435. @param user_status
  436. @param req_handle
  437. @param option
  438. **/
  439. void DSQL_free_statement(thread_db* tdbb, dsql_req* request, USHORT option)
  440. {
  441. SET_TDBB(tdbb);
  442. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  443. if (option & DSQL_drop) {
  444. // Release everything associated with the request
  445. release_request(tdbb, request, true);
  446. }
  447. else if (option & DSQL_unprepare) {
  448. // Release everything but the request itself
  449. release_request(tdbb, request, false);
  450. }
  451. else if (option & DSQL_close) {
  452. // Just close the cursor associated with the request
  453. if (reqTypeWithCursor(request->req_type))
  454. {
  455. if (!(request->req_flags & REQ_cursor_open))
  456. {
  457. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-501) <<
  458. Arg::Gds(isc_dsql_cursor_close_err));
  459. }
  460. close_cursor(tdbb, request);
  461. }
  462. }
  463. }
  464. /**
  465. DSQL_insert
  466. @brief Insert next record into a dynamic SQL cursor
  467. @param user_status
  468. @param req_handle
  469. @param blr_length
  470. @param blr
  471. @param msg_type OBSOLETE
  472. @param msg_length
  473. @param dsql_msg
  474. **/
  475. void DSQL_insert(thread_db* tdbb,
  476. dsql_req* request,
  477. USHORT blr_length, const UCHAR* blr,
  478. /*USHORT msg_type,*/ USHORT msg_length, const UCHAR* dsql_msg_buf)
  479. {
  480. SET_TDBB(tdbb);
  481. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  482. if (request->req_flags & REQ_orphan)
  483. {
  484. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
  485. Arg::Gds(isc_bad_req_handle));
  486. }
  487. // if the cursor isn't open, we've got a problem
  488. if (request->req_type == REQ_PUT_SEGMENT)
  489. {
  490. if (!(request->req_flags & REQ_cursor_open))
  491. {
  492. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
  493. Arg::Gds(isc_dsql_cursor_err) <<
  494. Arg::Gds(isc_dsql_cursor_not_open));
  495. }
  496. }
  497. dsql_msg* message = (dsql_msg*) request->req_receive;
  498. // Insure that the blr for the message is parsed, regardless of
  499. // whether anything is found by the call to receive.
  500. if (blr_length)
  501. parse_blr(blr_length, blr, msg_length, message->msg_parameters);
  502. if (request->req_type == REQ_PUT_SEGMENT) {
  503. // For put segment, use the user buffer and indicator directly.
  504. dsql_par* parameter = request->req_blob->blb_segment;
  505. const UCHAR* buffer = dsql_msg_buf + (IPTR) parameter->par_user_desc.dsc_address;
  506. BLB_put_segment(tdbb, request->req_blob->blb_blob, buffer,
  507. parameter->par_user_desc.dsc_length);
  508. }
  509. }
  510. /**
  511. DSQL_prepare
  512. @brief Prepare a statement for execution.
  513. @param user_status
  514. @param trans_handle
  515. @param req_handle
  516. @param length
  517. @param string
  518. @param dialect
  519. @param item_length
  520. @param items
  521. @param buffer_length
  522. @param buffer
  523. **/
  524. void DSQL_prepare(thread_db* tdbb,
  525. jrd_tra* transaction,
  526. dsql_req** req_handle,
  527. USHORT length, const TEXT* string, USHORT dialect,
  528. USHORT item_length, const UCHAR* items,
  529. USHORT buffer_length, UCHAR* buffer)
  530. {
  531. SET_TDBB(tdbb);
  532. dsql_req* const old_request = *req_handle;
  533. if (!old_request) {
  534. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
  535. Arg::Gds(isc_bad_req_handle));
  536. }
  537. dsql_dbb* database = old_request->req_dbb;
  538. if (!database) {
  539. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
  540. Arg::Gds(isc_bad_req_handle));
  541. }
  542. // check to see if old request has an open cursor
  543. if (old_request && (old_request->req_flags & REQ_cursor_open)) {
  544. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-519) <<
  545. Arg::Gds(isc_dsql_open_cursor_request));
  546. }
  547. dsql_req* request = NULL;
  548. try {
  549. // Figure out which parser version to use
  550. // Since the API to dsql8_prepare is public and can not be changed, there needs to
  551. // be a way to send the parser version to DSQL so that the parser can compare the keyword
  552. // version to the parser version. To accomplish this, the parser version is combined with
  553. // the client dialect and sent across that way. In dsql8_prepare_statement, the parser version
  554. // and client dialect are separated and passed on to their final destinations. The information
  555. // is combined as follows:
  556. // Dialect * 10 + parser_version
  557. //
  558. // and is extracted in dsql8_prepare_statement as follows:
  559. // parser_version = ((dialect *10)+parser_version)%10
  560. // client_dialect = ((dialect *10)+parser_version)/10
  561. //
  562. // For example, parser_version = 1 and client dialect = 1
  563. //
  564. // combined = (1 * 10) + 1 == 11
  565. //
  566. // parser = (combined) %10 == 1
  567. // dialect = (combined) / 19 == 1
  568. //
  569. // If the parser version is not part of the dialect, then assume that the
  570. // connection being made is a local classic connection.
  571. USHORT parser_version;
  572. if ((dialect / 10) == 0)
  573. parser_version = 2;
  574. else {
  575. parser_version = dialect % 10;
  576. dialect /= 10;
  577. }
  578. // Allocate a new request block and then prepare the request. We want to
  579. // keep the old request around, as is, until we know that we are able
  580. // to prepare the new one.
  581. // It would be really *nice* to know *why* we want to
  582. // keep the old request around -- 1994-October-27 David Schnepper
  583. // Because that's the client's allocated statement handle and we
  584. // don't want to trash the context in it -- 2001-Oct-27 Ann Harrison
  585. request = prepare(tdbb, database, transaction, length, string, dialect, parser_version);
  586. // Can not prepare a CREATE DATABASE/SCHEMA statement
  587. if (request->req_type == REQ_CREATE_DB)
  588. {
  589. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-530) <<
  590. Arg::Gds(isc_dsql_crdb_prepare_err));
  591. }
  592. request->req_flags |= REQ_prepared;
  593. // Now that we know that the new request exists, zap the old one.
  594. {
  595. Jrd::ContextPoolHolder context(tdbb, &old_request->req_pool);
  596. release_request(tdbb, old_request, true);
  597. }
  598. *req_handle = request;
  599. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  600. sql_info(tdbb, request, item_length, items, buffer_length, buffer);
  601. }
  602. catch (const Firebird::Exception&)
  603. {
  604. if (request)
  605. {
  606. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  607. release_request(tdbb, request, true);
  608. }
  609. throw;
  610. }
  611. }
  612. /**
  613. DSQL_set_cursor_name
  614. @brief Set a cursor name for a dynamic request
  615. @param user_status
  616. @param req_handle
  617. @param input_cursor
  618. @param type OBSOLETE
  619. **/
  620. void DSQL_set_cursor(thread_db* tdbb,
  621. dsql_req* request,
  622. const TEXT* input_cursor)
  623. //USHORT type)
  624. {
  625. SET_TDBB(tdbb);
  626. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  627. const size_t MAX_CURSOR_LENGTH = 132 - 1;
  628. Firebird::string cursor = input_cursor;
  629. if (cursor[0] == '\"')
  630. {
  631. // Quoted cursor names eh? Strip'em.
  632. // Note that "" will be replaced with ".
  633. // The code is very strange, because it doesn't check for "" really
  634. // and thus deletes one isolated " in the middle of the cursor.
  635. for (Firebird::string::iterator i = cursor.begin(); i < cursor.end(); ++i)
  636. {
  637. if (*i == '\"') {
  638. cursor.erase(i);
  639. }
  640. }
  641. }
  642. else // not quoted name
  643. {
  644. const Firebird::string::size_type i = cursor.find(' ');
  645. if (i != Firebird::string::npos)
  646. {
  647. cursor.resize(i);
  648. }
  649. cursor.upper();
  650. }
  651. USHORT length = (USHORT) fb_utils::name_length(cursor.c_str());
  652. if (length == 0) {
  653. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
  654. Arg::Gds(isc_dsql_decl_err) <<
  655. Arg::Gds(isc_dsql_cursor_invalid));
  656. }
  657. if (length > MAX_CURSOR_LENGTH) {
  658. length = MAX_CURSOR_LENGTH;
  659. }
  660. cursor.resize(length);
  661. // If there already is a different cursor by the same name, bitch
  662. const dsql_sym* symbol = HSHD_lookup(request->req_dbb, cursor.c_str(), length, SYM_cursor, 0);
  663. if (symbol)
  664. {
  665. if (request->req_cursor == symbol)
  666. return;
  667. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
  668. Arg::Gds(isc_dsql_decl_err) <<
  669. Arg::Gds(isc_dsql_cursor_redefined) << Arg::Str(symbol->sym_string));
  670. }
  671. // If there already is a cursor and its name isn't the same, ditto.
  672. // We already know there is no cursor by this name in the hash table
  673. if (!request->req_cursor) {
  674. request->req_cursor = MAKE_symbol(request->req_dbb, cursor.c_str(), length, SYM_cursor, request);
  675. }
  676. else {
  677. fb_assert(request->req_cursor != symbol);
  678. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
  679. Arg::Gds(isc_dsql_decl_err) <<
  680. Arg::Gds(isc_dsql_cursor_redefined) << Arg::Str(request->req_cursor->sym_string));
  681. }
  682. }
  683. /**
  684. DSQL_sql_info
  685. @brief Provide information on dsql statement
  686. @param user_status
  687. @param req_handle
  688. @param item_length
  689. @param items
  690. @param info_length
  691. @param info
  692. **/
  693. void DSQL_sql_info(thread_db* tdbb,
  694. dsql_req* request,
  695. USHORT item_length, const UCHAR* items,
  696. ULONG info_length, UCHAR* info)
  697. {
  698. SET_TDBB(tdbb);
  699. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  700. sql_info(tdbb, request, item_length, items, info_length, info);
  701. }
  702. /**
  703. close_cursor
  704. @brief Close an open cursor.
  705. @param request
  706. @param tdbb
  707. **/
  708. static void close_cursor(thread_db* tdbb, dsql_req* request)
  709. {
  710. SET_TDBB(tdbb);
  711. Attachment* attachment = request->req_dbb->dbb_attachment;
  712. if (request->req_request)
  713. {
  714. ThreadStatusGuard status_vector(tdbb);
  715. try
  716. {
  717. if (request->req_type == REQ_GET_SEGMENT || request->req_type == REQ_PUT_SEGMENT)
  718. {
  719. BLB_close(tdbb, request->req_blob->blb_blob);
  720. request->req_blob->blb_blob = NULL;
  721. }
  722. else
  723. {
  724. // Report some remaining fetches if any
  725. if (request->req_fetch_baseline)
  726. {
  727. TraceDSQLFetch trace(attachment, request);
  728. trace.fetch(true, res_successful);
  729. }
  730. if (request->req_traced && TraceManager::need_dsql_free(attachment))
  731. {
  732. TraceSQLStatementImpl stmt(request, NULL);
  733. TraceManager::event_dsql_free(attachment, &stmt, DSQL_close);
  734. }
  735. JRD_unwind_request(tdbb, request->req_request, 0);
  736. }
  737. }
  738. catch (Firebird::Exception&)
  739. {
  740. }
  741. }
  742. request->req_flags &= ~REQ_cursor_open;
  743. TRA_unlink_cursor(request->req_transaction, request);
  744. }
  745. /**
  746. convert
  747. @brief Convert a number to VAX form -- least significant bytes first.
  748. Return the length.
  749. @param number
  750. @param buffer
  751. **/
  752. // CVC: This routine should disappear in favor of a centralized function.
  753. static USHORT convert( SLONG number, UCHAR* buffer)
  754. {
  755. const UCHAR* p;
  756. #ifndef WORDS_BIGENDIAN
  757. p = (UCHAR*) &number;
  758. *buffer++ = *p++;
  759. *buffer++ = *p++;
  760. *buffer++ = *p++;
  761. *buffer++ = *p++;
  762. #else
  763. p = (UCHAR*) (&number + 1);
  764. *buffer++ = *--p;
  765. *buffer++ = *--p;
  766. *buffer++ = *--p;
  767. *buffer++ = *--p;
  768. #endif
  769. return 4;
  770. }
  771. /**
  772. execute_blob
  773. @brief Open or create a blob.
  774. @param tdbb
  775. @param request
  776. @param in_blr_length
  777. @param in_blr
  778. @param in_msg_length
  779. @param in_msg
  780. @param out_blr_length
  781. @param out_blr
  782. @param out_msg_length
  783. @param out_msg
  784. **/
  785. static void execute_blob(thread_db* tdbb,
  786. dsql_req* request,
  787. USHORT in_blr_length,
  788. const UCHAR* in_blr,
  789. USHORT in_msg_length,
  790. const UCHAR* in_msg,
  791. USHORT out_blr_length,
  792. UCHAR* out_blr,
  793. USHORT out_msg_length,
  794. UCHAR* out_msg)
  795. {
  796. UCHAR bpb[24];
  797. dsql_blb* blob = request->req_blob;
  798. map_in_out(request, blob->blb_open_in_msg, in_blr_length, in_blr, in_msg_length, NULL, in_msg);
  799. UCHAR* p = bpb;
  800. *p++ = isc_bpb_version1;
  801. SSHORT filter = filter_sub_type(blob->blb_to);
  802. if (filter) {
  803. *p++ = isc_bpb_target_type;
  804. *p++ = 2;
  805. *p++ = static_cast<UCHAR>(filter);
  806. *p++ = filter >> 8;
  807. }
  808. filter = filter_sub_type(blob->blb_from);
  809. if (filter) {
  810. *p++ = isc_bpb_source_type;
  811. *p++ = 2;
  812. *p++ = static_cast<UCHAR>(filter);
  813. *p++ = filter >> 8;
  814. }
  815. USHORT bpb_length = p - bpb;
  816. if (bpb_length == 1) {
  817. bpb_length = 0;
  818. }
  819. dsql_par* parameter = blob->blb_blob_id;
  820. const dsql_par* null = parameter->par_null;
  821. if (request->req_type == REQ_GET_SEGMENT)
  822. {
  823. bid* blob_id = (bid*) parameter->par_desc.dsc_address;
  824. if (null && *((SSHORT *) null->par_desc.dsc_address) < 0) {
  825. memset(blob_id, 0, sizeof(bid));
  826. }
  827. request->req_blob->blb_blob =
  828. BLB_open2(tdbb, request->req_transaction, blob_id, bpb_length, bpb, true);
  829. }
  830. else
  831. {
  832. request->req_request = NULL;
  833. bid* blob_id = (bid*) parameter->par_desc.dsc_address;
  834. memset(blob_id, 0, sizeof(bid));
  835. request->req_blob->blb_blob =
  836. BLB_create2(tdbb, request->req_transaction, blob_id, bpb_length, bpb);
  837. map_in_out(NULL, blob->blb_open_out_msg, out_blr_length, out_blr, out_msg_length, out_msg);
  838. }
  839. }
  840. /**
  841. execute_immediate
  842. @brief Common part of prepare and execute a statement.
  843. @param tdbb
  844. @param attachment
  845. @param tra_handle
  846. @param length
  847. @param string
  848. @param dialect
  849. @param in_blr_length
  850. @param in_blr
  851. @param in_msg_type OBSOLETE
  852. @param in_msg_length
  853. @param in_msg
  854. @param out_blr_length
  855. @param out_blr
  856. @param out_msg_type OBSOLETE
  857. @param out_msg_length
  858. @param out_msg
  859. **/
  860. static void execute_immediate(thread_db* tdbb,
  861. Attachment* attachment,
  862. jrd_tra** tra_handle,
  863. USHORT length, const TEXT* string, USHORT dialect,
  864. USHORT in_blr_length, const UCHAR* in_blr,
  865. /*USHORT in_msg_type,*/ USHORT in_msg_length, const UCHAR* in_msg,
  866. USHORT out_blr_length, UCHAR* out_blr,
  867. /*USHORT out_msg_type,*/ USHORT out_msg_length, UCHAR* out_msg)
  868. {
  869. SET_TDBB(tdbb);
  870. dsql_dbb* const database = init(attachment);
  871. dsql_req* request = NULL;
  872. try {
  873. // Figure out which parser version to use
  874. // Since the API to dsql8_execute_immediate is public and can not be changed, there needs to
  875. // be a way to send the parser version to DSQL so that the parser can compare the keyword
  876. // version to the parser version. To accomplish this, the parser version is combined with
  877. // the client dialect and sent across that way. In dsql8_execute_immediate, the parser version
  878. // and client dialect are separated and passed on to their final destinations. The information
  879. // is combined as follows:
  880. // Dialect * 10 + parser_version
  881. //
  882. // and is extracted in dsql8_execute_immediate as follows:
  883. // parser_version = ((dialect *10)+parser_version)%10
  884. // client_dialect = ((dialect *10)+parser_version)/10
  885. //
  886. // For example, parser_version = 1 and client dialect = 1
  887. //
  888. // combined = (1 * 10) + 1 == 11
  889. //
  890. // parser = (combined) %10 == 1
  891. // dialect = (combined) / 19 == 1
  892. //
  893. // If the parser version is not part of the dialect, then assume that the
  894. // connection being made is a local classic connection.
  895. USHORT parser_version;
  896. if ((dialect / 10) == 0)
  897. parser_version = 2;
  898. else {
  899. parser_version = dialect % 10;
  900. dialect /= 10;
  901. }
  902. request = prepare(tdbb, database, *tra_handle, length, string, dialect, parser_version);
  903. // Only allow NULL trans_handle if we're starting a transaction
  904. if (!*tra_handle && request->req_type != REQ_START_TRANS)
  905. {
  906. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
  907. Arg::Gds(isc_bad_trans_handle));
  908. }
  909. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  910. // A select with a non zero output length is a singleton select
  911. const bool singleton = (request->req_type == REQ_SELECT && out_msg_length != 0);
  912. execute_request(tdbb, request, tra_handle,
  913. in_blr_length, in_blr, in_msg_length, in_msg,
  914. out_blr_length, out_blr, out_msg_length, out_msg,
  915. singleton);
  916. release_request(tdbb, request, true);
  917. }
  918. catch (const Firebird::Exception&)
  919. {
  920. if (request)
  921. {
  922. Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
  923. release_request(tdbb, request, true);
  924. }
  925. throw;
  926. }
  927. }
  928. /**
  929. execute_request
  930. @brief Execute a dynamic SQL statement.
  931. @param tdbb
  932. @param request
  933. @param trans_handle
  934. @param in_blr_length
  935. @param in_blr
  936. @param in_msg_length
  937. @param in_msg
  938. @param out_blr_length
  939. @param out_blr
  940. @param out_msg_length
  941. @param out_msg
  942. @param singleton
  943. **/
  944. static void execute_request(thread_db* tdbb,
  945. dsql_req* request,
  946. jrd_tra** tra_handle,
  947. USHORT in_blr_length, const UCHAR* in_blr,
  948. USHORT in_msg_length, const UCHAR* in_msg,
  949. USHORT out_blr_length, UCHAR* out_blr,
  950. USHORT out_msg_length, UCHAR* out_msg,
  951. bool singleton)
  952. {
  953. request->req_transaction = *tra_handle;
  954. switch (request->req_type)
  955. {
  956. case REQ_START_TRANS:
  957. JRD_start_transaction(tdbb, &request->req_transaction, 1, &request->req_dbb->dbb_attachment,
  958. request->req_blr_data.getCount(), request->req_blr_data.begin());
  959. *tra_handle = request->req_transaction;
  960. return;
  961. case REQ_COMMIT:
  962. JRD_commit_transaction(tdbb, &request->req_transaction);
  963. *tra_handle = NULL;
  964. return;
  965. case REQ_COMMIT_RETAIN:
  966. JRD_commit_retaining(tdbb, &request->req_transaction);
  967. return;
  968. case REQ_ROLLBACK:
  969. JRD_rollback_transaction(tdbb, &request->req_transaction);
  970. *tra_handle = NULL;
  971. return;
  972. case REQ_ROLLBACK_RETAIN:
  973. JRD_rollback_retaining(tdbb, &request->req_transaction);
  974. return;
  975. case REQ_CREATE_DB:
  976. case REQ_DDL:
  977. {
  978. TraceDSQLExecute trace(request->req_dbb->dbb_attachment, request);
  979. DDL_execute(request);
  980. trace.finish(false, res_successful);
  981. return;
  982. }
  983. case REQ_GET_SEGMENT:
  984. execute_blob(tdbb, request,
  985. in_blr_length, in_blr, in_msg_length, in_msg,
  986. out_blr_length, out_blr, out_msg_length, out_msg);
  987. return;
  988. case REQ_PUT_SEGMENT:
  989. execute_blob(tdbb, request,
  990. in_blr_length, in_blr, in_msg_length, in_msg,
  991. out_blr_length, out_blr, out_msg_length, out_msg);
  992. return;
  993. case REQ_SELECT:
  994. case REQ_SELECT_UPD:
  995. case REQ_EMBED_SELECT:
  996. case REQ_INSERT:
  997. case REQ_UPDATE:
  998. case REQ_UPDATE_CURSOR:
  999. case REQ_DELETE:
  1000. case REQ_DELETE_CURSOR:
  1001. case REQ_EXEC_PROCEDURE:
  1002. case REQ_SET_GENERATOR:
  1003. case REQ_SAVEPOINT:
  1004. case REQ_EXEC_BLOCK:
  1005. case REQ_SELECT_BLOCK:
  1006. break;
  1007. default:
  1008. // Catch invalid request types
  1009. fb_assert(false);
  1010. }
  1011. // If there is no data required, just start the request
  1012. dsql_msg* message = request->req_send;
  1013. if (message)
  1014. map_in_out(request, message, in_blr_length, in_blr, in_msg_length, NULL, in_msg);
  1015. // we need to map_in_out before tracing of execution start to let trace
  1016. // manager know statement parameters values
  1017. TraceDSQLExecute trace(request->req_dbb->dbb_attachment, request);
  1018. if (!message)
  1019. JRD_start(tdbb, request->req_request, request->req_transaction, 0);
  1020. else
  1021. {
  1022. JRD_start_and_send(tdbb, request->req_request, request->req_transaction, message->msg_number,
  1023. message->msg_length, message->msg_buffer,
  1024. 0);
  1025. }
  1026. // Selectable execute block should get the "proc fetch" flag assigned,
  1027. // which ensures that the savepoint stack is preserved while suspending
  1028. if (request->req_type == REQ_SELECT_BLOCK)
  1029. {
  1030. fb_assert(request->req_request);
  1031. request->req_request->req_flags |= req_proc_fetch;
  1032. }
  1033. // REQ_EXEC_BLOCK has no outputs so there are no out_msg
  1034. // supplied from client side, but REQ_EXEC_BLOCK requires
  1035. // 2-byte message for EOS synchronization
  1036. const bool isBlock = (request->req_type == REQ_EXEC_BLOCK);
  1037. message = request->req_receive;
  1038. if ((out_msg_length && message) || isBlock)
  1039. {
  1040. char temp_buffer[FB_DOUBLE_ALIGN * 2];
  1041. dsql_msg temp_msg;
  1042. // Insure that the blr for the message is parsed, regardless of
  1043. // whether anything is found by the call to receive.
  1044. if (out_msg_length && out_blr_length) {
  1045. parse_blr(out_blr_length, out_blr, out_msg_length, message->msg_parameters);
  1046. }
  1047. else if (!out_msg_length && isBlock) {
  1048. message = &temp_msg;
  1049. message->msg_number = 1;
  1050. message->msg_length = 2;
  1051. message->msg_buffer = (UCHAR*) FB_ALIGN((U_IPTR) temp_buffer, FB_DOUBLE_ALIGN);
  1052. }
  1053. JRD_receive(tdbb, request->req_request, message->msg_number, message->msg_length,
  1054. message->msg_buffer, 0);
  1055. if (out_msg_length)
  1056. map_in_out(NULL, message, 0, out_blr, out_msg_length, out_msg);
  1057. // if this is a singleton select, make sure there's in fact one record
  1058. if (singleton)
  1059. {
  1060. USHORT counter;
  1061. // Create a temp message buffer and try two more receives.
  1062. // If both succeed then the first is the next record and the
  1063. // second is either another record or the end of record message.
  1064. // In either case, there's more than one record.
  1065. UCHAR* message_buffer = (UCHAR*) gds__alloc((ULONG) message->msg_length);
  1066. ISC_STATUS status = FB_SUCCESS;
  1067. ISC_STATUS_ARRAY localStatus;
  1068. for (counter = 0; counter < 2 && !status; counter++)
  1069. {
  1070. AutoSetRestore<ISC_STATUS*> autoStatus(&tdbb->tdbb_status_vector, localStatus);
  1071. fb_utils::init_status(localStatus);
  1072. try
  1073. {
  1074. JRD_receive(tdbb, request->req_request, message->msg_number,
  1075. message->msg_length, message_buffer, 0);
  1076. status = FB_SUCCESS;
  1077. }
  1078. catch (Firebird::Exception&)
  1079. {
  1080. status = tdbb->tdbb_status_vector[1];
  1081. }
  1082. }
  1083. gds__free(message_buffer);
  1084. // two successful receives means more than one record
  1085. // a req_sync error on the first pass above means no records
  1086. // a non-req_sync error on any of the passes above is an error
  1087. if (!status)
  1088. status_exception::raise(Arg::Gds(isc_sing_select_err));
  1089. else if (status == isc_req_sync && counter == 1)
  1090. status_exception::raise(Arg::Gds(isc_stream_eof));
  1091. else if (status != isc_req_sync)
  1092. status_exception::raise(localStatus);
  1093. }
  1094. }
  1095. UCHAR buffer[20]; // Not used after retrieved
  1096. if (request->req_type == REQ_UPDATE_CURSOR)
  1097. {
  1098. sql_info(tdbb, request, sizeof(sql_records_info), sql_records_info, sizeof(buffer), buffer);
  1099. if (!request->req_updates)
  1100. {
  1101. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) <<
  1102. Arg::Gds(isc_deadlock) <<
  1103. Arg::Gds(isc_update_conflict));
  1104. }
  1105. }
  1106. else if (request->req_type == REQ_DELETE_CURSOR)
  1107. {
  1108. sql_info(tdbb, request, sizeof(sql_records_info), sql_records_info, sizeof(buffer), buffer);
  1109. if (!request->req_deletes)
  1110. {
  1111. ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) <<
  1112. Arg::Gds(isc_deadlock) <<
  1113. Arg::Gds(isc_update_conflict));
  1114. }
  1115. }
  1116. const bool have_cursor = reqTypeWithCursor(request->req_type) && !singleton;
  1117. trace.finish(have_cursor, res_successful);
  1118. }
  1119. /**
  1120. filter_sub_type
  1121. @brief Determine the sub_type to use in filtering
  1122. a blob.
  1123. @param node
  1124. **/
  1125. static SSHORT filter_sub_type(const dsql_nod* node)
  1126. {
  1127. if (node->nod_type == nod_constant)
  1128. return (SSHORT) node->getSlong();
  1129. const dsql_par* parameter = (dsql_par*) node->nod_arg[e_par_parameter];
  1130. const dsql_par* null = parameter->par_null;
  1131. if (null)
  1132. {
  1133. if (*((SSHORT *) null->par_desc.dsc_address))
  1134. return 0;
  1135. }
  1136. return *((SSHORT *) parameter->par_desc.dsc_address);
  1137. }
  1138. /**
  1139. get_indices
  1140. @brief Retrieve the indices from the index tree in
  1141. the request info buffer (explain_ptr), and print them out
  1142. in the plan buffer. Return true on success and false on failure.
  1143. @param explain_length_ptr
  1144. @param explain_ptr
  1145. @param plan_length_ptr
  1146. @param plan_ptr
  1147. **/
  1148. static bool get_indices(SLONG* explain_length_ptr, const UCHAR** explain_ptr,
  1149. SLONG* plan_length_ptr, SCHAR** plan_ptr)
  1150. {
  1151. USHORT length;
  1152. SLONG explain_length = *explain_length_ptr;
  1153. const UCHAR* explain = *explain_ptr;
  1154. SLONG& plan_length = *plan_length_ptr;
  1155. SCHAR*& plan = *plan_ptr;
  1156. // go through the index tree information, just
  1157. // extracting the indices used
  1158. explain_length--;
  1159. switch (*explain++)
  1160. {
  1161. case isc_info_rsb_and:
  1162. case isc_info_rsb_or:
  1163. if (!get_indices(&explain_length, &explain, &plan_length, &plan))
  1164. return false;
  1165. if (!get_indices(&explain_length, &explain, &plan_length, &plan))
  1166. return false;
  1167. break;
  1168. case isc_info_rsb_dbkey:
  1169. break;
  1170. case isc_info_rsb_index:
  1171. explain_length--;
  1172. length = *explain++;
  1173. // if this isn't the first index, put out a comma
  1174. if (plan[-1] != '(' && plan[-1] != ' ') {
  1175. plan_length -= 2;
  1176. if (plan_length < 0)
  1177. return false;
  1178. *plan++ = ',';
  1179. *plan++ = ' ';
  1180. }
  1181. // now put out the index name
  1182. if ((plan_length -= length) < 0)
  1183. return false;
  1184. explain_length -= length;
  1185. while (length--)
  1186. *plan++ = *explain++;
  1187. break;
  1188. default:
  1189. return false;
  1190. }
  1191. *explain_length_ptr = explain_length;
  1192. *explain_ptr = explain;
  1193. //*plan_length_ptr = plan_length;
  1194. //*plan_ptr = plan;
  1195. return true;
  1196. }
  1197. /**
  1198. DSQL_get_plan_info
  1199. @brief Get the access plan for the request and turn
  1200. it into a textual representation suitable for
  1201. human reading.
  1202. @param request
  1203. @param buffer_length
  1204. @param out_buffer
  1205. @param realloc
  1206. **/
  1207. ULONG DSQL_get_plan_info(thread_db* tdbb,
  1208. const dsql_req* request,
  1209. SLONG buffer_length,
  1210. SCHAR** out_buffer,
  1211. const bool realloc)
  1212. {
  1213. if (!request->req_request) // DDL
  1214. return 0;
  1215. Firebird::HalfStaticArray<UCHAR, BUFFER_LARGE> explain_buffer;
  1216. explain_buffer.resize(BUFFER_LARGE);
  1217. // get the access path info for the underlying request from the engine
  1218. try
  1219. {
  1220. JRD_request_info(tdbb, request->req_request, 0,
  1221. sizeof(explain_info), explain_info,
  1222. explain_buffer.getCount(), explain_buffer.begin());
  1223. if (explain_buffer[0] == isc_info_truncated)
  1224. {
  1225. explain_buffer.resize(MAX_USHORT);
  1226. JRD_request_info(tdbb, request->req_request, 0,
  1227. sizeof(explain_info), explain_info,
  1228. explain_buffer.getCount(), explain_buffer.begin());
  1229. if (explain_buffer[0] == isc_info_truncated)
  1230. {
  1231. return 0;
  1232. }
  1233. }
  1234. }
  1235. catch (Firebird::Exception&)
  1236. {
  1237. return 0;
  1238. }
  1239. SCHAR* buffer_ptr = *out_buffer;
  1240. SCHAR* plan;
  1241. for (int i = 0; i < 2; i++)
  1242. {
  1243. const UCHAR* explain = explain_buffer.begin();
  1244. if (*explain++ != isc_info_access_path)
  1245. {
  1246. return 0;
  1247. }
  1248. SLONG explain_length = (ULONG) *explain++;
  1249. explain_length += (ULONG) (*explain++) << 8;
  1250. plan = buffer_ptr;
  1251. // CVC: What if we need to do 2nd pass? Those variables were only initialized
  1252. // at the begining of the function hence they had trash the second time.
  1253. USHORT join_count = 0, level = 0;
  1254. const ULONG full_len = buffer_length;
  1255. memset(plan, 0, full_len);
  1256. // This is testing code for the limit case,
  1257. // please do not enable for normal operations.
  1258. /*
  1259. if (full_len == ULONG(MAX_USHORT) - 4)
  1260. {
  1261. const size_t test_offset = 55000;
  1262. memset(plan, '.', test_offset);
  1263. plan += test_offset;
  1264. buffer_length -= test_offset;
  1265. }
  1266. */
  1267. // keep going until we reach the end of the explain info
  1268. while (explain_length > 0 && buffer_length > 0)
  1269. {
  1270. if (!get_rsb_item(&explain_length, &explain, &buffer_length, &plan, &join_count, &level))
  1271. {
  1272. // don't allocate buffer of the same length second time
  1273. // and let user know plan is incomplete
  1274. if (buffer_ptr != *out_buffer ||
  1275. (!realloc && full_len == ULONG(MAX_USHORT) - 4))
  1276. {
  1277. const ptrdiff_t diff = buffer_ptr + full_len - plan;
  1278. if (diff < 3) {
  1279. plan -= 3 - diff;
  1280. }
  1281. fb_assert(plan > buffer_ptr);
  1282. *plan++ = '.';
  1283. *plan++ = '.';
  1284. *plan++ = '.';
  1285. if (!realloc)
  1286. return plan - buffer_ptr;
  1287. ++i;
  1288. break;
  1289. }
  1290. if (!realloc)
  1291. return full_len - buffer_length;
  1292. // assume we have run out of room in the buffer, try again with a larger one
  1293. const size_t new_length = MAX_USHORT;
  1294. char* const temp = static_cast<char*>(gds__alloc(new_length));
  1295. if (!temp) {
  1296. // NOMEM. Do not attempt one more try
  1297. i++;
  1298. break;
  1299. }
  1300. buffer_ptr = temp;
  1301. buffer_length = (SLONG) new_length;
  1302. break;
  1303. }
  1304. }
  1305. if (buffer_ptr == *out_buffer)
  1306. break;
  1307. }
  1308. *out_buffer = buffer_ptr;
  1309. return plan - *out_buffer;
  1310. }
  1311. /**
  1312. get_request_info
  1313. @brief Get the records updated/deleted for record
  1314. @param request
  1315. @param buffer_length
  1316. @param buffer
  1317. **/
  1318. static USHORT get_request_info(thread_db* tdbb,
  1319. dsql_req* request,
  1320. SLONG buffer_length,
  1321. UCHAR* buffer)
  1322. {
  1323. if (!request->req_request) // DDL
  1324. return 0;
  1325. // get the info for the request from the engine
  1326. try
  1327. {
  1328. JRD_request_info(tdbb, request->req_request, 0,
  1329. sizeof(record_info), record_info,
  1330. buffer_length, buffer);
  1331. }
  1332. catch (Firebird::Exception&)
  1333. {
  1334. return 0;
  1335. }
  1336. const UCHAR* data = buffer;
  1337. request->req_updates = request->req_deletes = 0;
  1338. request->req_selects = request->req_inserts = 0;
  1339. UCHAR p;
  1340. while ((p = *data++) != isc_info_end)
  1341. {
  1342. const USHORT data_length = static_cast<USHORT>(gds__vax_integer(data, 2));
  1343. data += 2;
  1344. switch (p)
  1345. {
  1346. case isc_info_req_update_count:
  1347. request->req_updates = gds__vax_integer(data, data_length);
  1348. break;
  1349. case isc_info_req_delete_count:
  1350. request->req_deletes = gds__vax_integer(data, data_length);
  1351. break;
  1352. case isc_info_req_select_count:
  1353. request->req_selects = gds__vax_integer(data, data_length);
  1354. break;
  1355. case isc_info_req_insert_count:
  1356. request->req_inserts = gds__vax_integer(data, data_length);
  1357. break;
  1358. default:
  1359. break;
  1360. }
  1361. data += data_length;
  1362. }
  1363. return data - buffer;
  1364. }
  1365. /**
  1366. get_rsb_item
  1367. @brief Use recursion to print out a reverse-polish
  1368. access plan of joins and join types. Return true on success
  1369. and false on failure
  1370. @param explain_length_ptr
  1371. @param explain_ptr
  1372. @param plan_length_ptr
  1373. @param plan_ptr
  1374. @param parent_join_count
  1375. @param level_ptr
  1376. **/
  1377. static bool get_rsb_item(SLONG* explain_length_ptr,
  1378. const UCHAR** explain_ptr,
  1379. SLONG* plan_length_ptr,
  1380. SCHAR** plan_ptr,
  1381. USHORT* parent_join_count,
  1382. USHORT* level_ptr)
  1383. {
  1384. const SCHAR* p;
  1385. SSHORT rsb_type;
  1386. SLONG explain_length = *explain_length_ptr;
  1387. const UCHAR* explain = *explain_ptr;
  1388. SLONG& plan_length = *plan_length_ptr;
  1389. SCHAR*& plan = *plan_ptr;
  1390. explain_length--;
  1391. switch (*explain++)
  1392. {
  1393. case isc_info_rsb_begin:
  1394. if (!*level_ptr)
  1395. {
  1396. // put out the PLAN prefix
  1397. p = "\nPLAN ";
  1398. if ((plan_length -= strlen(p)) < 0)
  1399. return false;
  1400. while (*p)
  1401. *plan++ = *p++;
  1402. }
  1403. (*level_ptr)++;
  1404. break;
  1405. case isc_info_rsb_end:
  1406. if (*level_ptr) {
  1407. (*level_ptr)--;
  1408. }
  1409. // else --*parent_join_count; ???
  1410. break;
  1411. case isc_info_rsb_relation:
  1412. // for the single relation case, initiate
  1413. // the relation with a parenthesis
  1414. if (!*parent_join_count) {
  1415. if (--plan_length < 0)
  1416. return false;
  1417. *plan++ = '(';
  1418. }
  1419. // if this isn't the first relation, put out a comma
  1420. if (plan[-1] != '(') {
  1421. plan_length -= 2;
  1422. if (plan_length < 0)
  1423. return false;
  1424. *plan++ = ',';
  1425. *plan++ = ' ';
  1426. }
  1427. // put out the relation name
  1428. { // scope to keep length local.
  1429. explain_length--;
  1430. SSHORT length = (USHORT) *explain++;
  1431. explain_length -= length;
  1432. if ((plan_length -= length) < 0)
  1433. return false;
  1434. while (length--)
  1435. *plan++ = *explain++;
  1436. } // scope
  1437. break;
  1438. case isc_info_rsb_type:
  1439. explain_length--;
  1440. // for stream types which have multiple substreams, print out
  1441. // the stream type and recursively print out the substreams so
  1442. // we will know where to put the parentheses
  1443. switch (rsb_type = *explain++)
  1444. {
  1445. case isc_info_rsb_union:
  1446. case isc_info_rsb_recursive:
  1447. // put out all the substreams of the join
  1448. { // scope to have union_count, union_level and union_join_count local.
  1449. explain_length--;
  1450. fb_assert(*explain > 0U);
  1451. USHORT union_count = (USHORT) *explain++ - 1;
  1452. // finish the first union member
  1453. USHORT union_level = *level_ptr;
  1454. USHORT union_join_count = 0;
  1455. while (explain_length > 0 && plan_length > 0)
  1456. {
  1457. if (!get_rsb_item(&explain_length, &explain, &plan_length, &plan,
  1458. &union_join_count, &union_level))
  1459. {
  1460. return false;
  1461. }
  1462. if (union_level == *level_ptr)
  1463. break;
  1464. }
  1465. // for the rest of the members, start the level at 0 so each
  1466. // gets its own "PLAN ... " line
  1467. while (union_count)
  1468. {
  1469. union_join_count = 0;
  1470. union_level = 0;
  1471. while (explain_length > 0 && plan_length > 0)
  1472. {
  1473. if (!get_rsb_item(&explain_length, &explain, &plan_length,
  1474. &plan, &union_join_count, &union_level))
  1475. {
  1476. return false;
  1477. }
  1478. if (!union_level)
  1479. break;
  1480. }
  1481. union_count--;
  1482. }
  1483. } // scope
  1484. break;
  1485. case isc_info_rsb_cross:
  1486. case isc_info_rsb_left_cross:
  1487. case isc_info_rsb_merge:
  1488. // if this join is itself part of a join list,
  1489. // but not the first item, then put out a comma
  1490. if (*parent_join_count && plan[-1] != '(')
  1491. {
  1492. plan_length -= 2;
  1493. if (plan_length < 0)
  1494. return false;
  1495. *plan++ = ',';
  1496. *plan++ = ' ';
  1497. }
  1498. // put out the join type
  1499. if (rsb_type == isc_info_rsb_cross || rsb_type == isc_info_rsb_left_cross)
  1500. {
  1501. p = "JOIN (";
  1502. }
  1503. else {
  1504. p = "MERGE (";
  1505. }
  1506. if ((plan_length -= strlen(p)) < 0)
  1507. return false;
  1508. while (*p)
  1509. *plan++ = *p++;
  1510. // put out all the substreams of the join
  1511. explain_length--;
  1512. { // scope to have join_count local.
  1513. USHORT join_count = (USHORT) *explain++;
  1514. while (join_count && explain_length > 0 && plan_length > 0)
  1515. {
  1516. if (!get_rsb_item(&explain_length, &explain, &plan_length,
  1517. &plan, &join_count, level_ptr))
  1518. {
  1519. return false;
  1520. }
  1521. // CVC: Here's the additional stop condition.
  1522. if (!*level_ptr) {
  1523. break;
  1524. }
  1525. }
  1526. } // scope
  1527. // put out the final parenthesis for the join
  1528. if (--plan_length < 0)
  1529. return false;
  1530. *plan++ = ')';
  1531. // this qualifies as a stream, so decrement the join count
  1532. if (*parent_join_count)
  1533. -- * parent_join_count;
  1534. break;
  1535. case isc_info_rsb_indexed:
  1536. case isc_info_rsb_navigate:
  1537. case isc_info_rsb_sequential:
  1538. case isc_info_rsb_ext_sequential:
  1539. case isc_info_rsb_ext_indexed:
  1540. case isc_info_rsb_virt_sequential:
  1541. switch (rsb_type)
  1542. {
  1543. case isc_info_rsb_indexed:
  1544. case isc_info_rsb_ext_indexed:
  1545. p = " INDEX (";
  1546. break;
  1547. case isc_info_rsb_navigate:
  1548. p = " ORDER ";
  1549. break;
  1550. default:
  1551. p = " NATURAL";
  1552. }
  1553. if ((plan_length -= strlen(p)) < 0)
  1554. return false;
  1555. while (*p)
  1556. *plan++ = *p++;
  1557. // print out additional index information
  1558. if (rsb_type == isc_info_rsb_indexed || rsb_type == isc_info_rsb_navigate ||
  1559. rsb_type == isc_info_rsb_ext_indexed)
  1560. {
  1561. if (!get_indices(&explain_length, &explain, &plan_length, &plan))
  1562. return false;
  1563. }
  1564. if (rsb_type == isc_info_rsb_navigate && *explain == isc_info_rsb_indexed)
  1565. {
  1566. USHORT idx_count = 1;
  1567. if (!get_rsb_item(&explain_length, &explain, &plan_length, &plan, &idx_count, level_ptr))
  1568. {
  1569. return false;
  1570. }
  1571. }
  1572. if (rsb_type == isc_info_rsb_indexed || rsb_type == isc_info_rsb_ext_indexed)
  1573. {
  1574. if (--plan_length < 0)
  1575. return false;
  1576. *plan++ = ')';
  1577. }
  1578. // detect the end of a single relation and put out a final parenthesis
  1579. if (!*parent_join_count)
  1580. {
  1581. if (--plan_length < 0)
  1582. return false;
  1583. *plan++ = ')';
  1584. }
  1585. // this also qualifies as a stream, so decrement the join count
  1586. if (*parent_join_count)
  1587. -- * parent_join_count;
  1588. break;
  1589. case isc_info_rsb_sort:
  1590. // if this sort is on behalf of a union, don't bother to
  1591. // print out the sort, because unions handle the sort on all
  1592. // substreams at once, and a plan maps to each substream
  1593. // in the union, so the sort doesn't really apply to a particular plan
  1594. if (explain_length > 2 && (explain[0] == isc_info_rsb_begin) &&
  1595. (explain[1] == isc_info_rsb_type) && (explain[2] == isc_info_rsb_union))
  1596. {
  1597. break;
  1598. }
  1599. // if this isn't the first item in the list, put out a comma
  1600. if (*parent_join_count && plan[-1] != '(')
  1601. {
  1602. plan_length -= 2;
  1603. if (plan_length < 0)
  1604. return false;
  1605. *plan++ = ',';
  1606. *plan++ = ' ';
  1607. }
  1608. p = "SORT (";
  1609. if ((plan_length -= strlen(p)) < 0)
  1610. return false;
  1611. while (*p)
  1612. *plan++ = *p++;
  1613. // the rsb_sort should always be followed by a begin...end block,
  1614. // allowing us to include everything inside the sort in parentheses
  1615. { // scope to have save_level local.
  1616. const USHORT save_level = *level_ptr;
  1617. while (explain_length > 0 && plan_length > 0)
  1618. {
  1619. if (!get_rsb_item(&explain_length, &explain, &plan_length,
  1620. &plan, parent_join_count,