4 * Copyright (c) 2012, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
8 #include "access/xact.h"
9 #include "catalog/namespace.h"
10 #include "catalog/pg_type.h"
11 #include "commands/copy.h"
12 #include "executor/spi.h"
13 #include "libpq/pqformat.h"
14 #include "mb/pg_wchar.h"
15 #include "tcop/tcopprot.h"
16 #include "utils/builtins.h"
17 #include "utils/lsyscache.h"
18 #include "utils/syscache.h"
19 #if PG_VERSION_NUM >= 90200
20 #include "catalog/pg_class.h"
23 #include "pg_dbms_stats.h"
25 #if PG_VERSION_NUM >= 90200
26 #define RELATION_PARAM_NUM 9
28 #define RELATION_PARAM_NUM 8
31 extern PGDLLIMPORT bool standard_conforming_strings;
33 PG_FUNCTION_INFO_V1(dbms_stats_import);
35 Datum dbms_stats_import(PG_FUNCTION_ARGS);
37 static void get_args(FunctionCallInfo fcinfo, char **nspname, char **relname,
38 char **attname, char **filename);
39 static void spi_exec_utility(const char *query);
40 static void spi_exec_query(const char *query, int nargs, Oid *argtypes,
41 SPIPlanPtr *plan, Datum *values, const char *nulls, int result);
42 static void import_stats_from_file(char *filename, char *nspname, char *relname,
47 * Import exported statistics from stdin or a file.
53 * 4) absolute path of source file, or 'stdin' (case insensitive)
56 dbms_stats_import(PG_FUNCTION_ARGS)
61 char *filename; /* filename, or NULL for STDIN */
67 SPIPlanPtr r_upd_plan = NULL;
68 SPIPlanPtr r_ins_plan = NULL;
69 SPIPlanPtr c_sel_plan = NULL;
70 SPIPlanPtr c_del_plan = NULL;
71 SPIPlanPtr c_ins_plan = NULL;
73 /* get validated arguments */
74 get_args(fcinfo, &nspname, &relname, &attname, &filename);
77 elog(DEBUG3, "%s() f=%s n=%s r=%s a=%s", __FUNCTION__,
78 filename ? filename : "(null)",
79 nspname ? nspname : "(null)",
80 relname ? relname : "(null)",
81 attname ? attname : "(null)");
85 if (ret != SPI_OK_CONNECT)
86 elog(ERROR, "pg_dbms_stats: SPI_connect => %d", ret);
88 /* lock dummy statistics tables. */
89 spi_exec_utility("LOCK dbms_stats._relation_stats_locked"
90 " IN SHARE UPDATE EXCLUSIVE MODE");
91 spi_exec_utility("LOCK dbms_stats._column_stats_locked"
92 " IN SHARE UPDATE EXCLUSIVE MODE");
95 * Create a temp table to save the statistics to import.
96 * This table should fit with the content of export files.
98 spi_exec_utility("CREATE TEMP TABLE dbms_stats_work_stats ("
99 "nspname name NOT NULL,"
100 "relname name NOT NULL,"
101 "relpages int4 NOT NULL,"
102 "reltuples float4 NOT NULL,"
103 #if PG_VERSION_NUM >= 90200
104 "relallvisible int4 NOT NULL,"
106 "curpages int4 NOT NULL,"
107 "last_analyze timestamp with time zone,"
108 "last_autoanalyze timestamp with time zone,"
110 "nspname_of_typename name,"
114 "stanullfrac float4,"
116 "stadistinct float4,"
121 #if PG_VERSION_NUM >= 90200
128 #if PG_VERSION_NUM >= 90200
131 "stanumbers1 float4[],"
132 "stanumbers2 float4[],"
133 "stanumbers3 float4[],"
134 "stanumbers4 float4[],"
135 #if PG_VERSION_NUM >= 90200
136 "stanumbers5 float4[],"
138 "stavalues1 dbms_stats.anyarray,"
139 "stavalues2 dbms_stats.anyarray,"
140 "stavalues3 dbms_stats.anyarray,"
141 "stavalues4 dbms_stats.anyarray"
142 #if PG_VERSION_NUM >= 90200
143 ",stavalues5 dbms_stats.anyarray"
147 /* load the statistics from export file to the temp table */
148 import_stats_from_file(filename, nspname, relname, attname);
150 /* Determine the Oid of local table from the tablename and schemaname. */
151 /* TODO 可視ページ数に対応する */
152 ret = SPI_execute("SELECT DISTINCT w.nspname, w.relname, c.oid, "
153 "w.relpages, w.reltuples, "
154 "w.curpages, w.last_analyze, w.last_autoanalyze "
155 #if PG_VERSION_NUM >= 90200
158 "FROM pg_catalog.pg_class c "
159 "JOIN pg_catalog.pg_namespace n "
160 "ON (c.relnamespace = n.oid) "
161 "RIGHT JOIN dbms_stats_work_stats w "
162 "ON (w.relname = c.relname AND w.nspname = n.nspname) "
163 "ORDER BY 1, 2", false, 0);
164 if (ret != SPI_OK_SELECT)
165 elog(ERROR, "pg_dbms_stats: SPI_execute => %d", ret);
168 * If there is no record in the staging table after loading source and
169 * deleting unnecessary records, we treat it as an error.
171 if (SPI_processed == 0)
172 elog(ERROR, "no per-table statistic data to be imported");
175 r_num = SPI_processed;
176 r_tups = SPI_tuptable->vals;
177 r_tupdesc = SPI_tuptable->tupdesc;
178 for (i = 0; i < r_num; i++)
185 char nulls[9] = {'t', 't', 't', 't', 't', 't', 't', 't', 't'};
186 Oid r_types[9] = {NAMEOID, NAMEOID, INT4OID, FLOAT4OID, INT4OID,
187 TIMESTAMPTZOID, TIMESTAMPTZOID, OIDOID, INT4OID};
188 Oid c_types[5] = {OIDOID, INT2OID, NAMEOID, NAMEOID,
195 values[0] = w_nspname = SPI_getbinval(r_tups[i], r_tupdesc, 1, &isnull);
196 values[1] = w_relname = SPI_getbinval(r_tups[i], r_tupdesc, 2, &isnull);
197 values[7] = w_relid = SPI_getbinval(r_tups[i], r_tupdesc, 3, &isnull);
200 elog(WARNING, "relation \"%s.%s\" does not exist",
201 DatumGetName(w_nspname)->data,
202 DatumGetName(w_relname)->data);
206 values[2] = SPI_getbinval(r_tups[i], r_tupdesc, 4, &isnull);
207 values[3] = SPI_getbinval(r_tups[i], r_tupdesc, 5, &isnull);
208 values[4] = SPI_getbinval(r_tups[i], r_tupdesc, 6, &isnull);
209 values[5] = SPI_getbinval(r_tups[i], r_tupdesc, 7, &isnull);
210 nulls[5] = isnull ? 'n' : 't';
211 values[6] = SPI_getbinval(r_tups[i], r_tupdesc, 8, &isnull);
212 nulls[6] = isnull ? 'n' : 't';
213 values[8] = SPI_getbinval(r_tups[i], r_tupdesc, 9, &isnull);
216 * First we try UPDATE with the oid. When no record matched, try
217 * INSERT. We can't use DELETE-then-INSERT method because we have FK
218 * on _relation_stats_locked so DELETE would delete child records in
219 * _column_stats_locked undesirably.
221 spi_exec_query("UPDATE dbms_stats._relation_stats_locked SET "
222 "relname = quote_ident($1) || '.' || quote_ident($2), "
223 "relpages = $3, reltuples = $4, "
224 #if PG_VERSION_NUM >= 90200
225 "relallvisible = $9, "
227 "curpages = $5, last_analyze = $6, last_autoanalyze = $7 "
229 RELATION_PARAM_NUM, r_types, &r_upd_plan, values, nulls,
231 if (SPI_processed == 0)
233 spi_exec_query("INSERT INTO dbms_stats._relation_stats_locked "
234 "(relname, relpages, reltuples, curpages, "
235 "last_analyze, last_autoanalyze, relid"
236 #if PG_VERSION_NUM >= 90200
239 ") VALUES (quote_ident($1) || '.' || quote_ident($2), "
240 "$3, $4, $5, $6, $7, $8"
241 #if PG_VERSION_NUM >= 90200
245 RELATION_PARAM_NUM, r_types, &r_ins_plan, values, nulls,
247 /* If we failed to insert, we can't proceed. */
248 if (SPI_processed != 1)
249 elog(ERROR, "failed to insert import data");
252 elog(DEBUG2, "\"%s.%s\" relation statistic import",
253 DatumGetName(w_nspname)->data, DatumGetName(w_relname)->data);
256 * Determine the attnum of the attribute with given name, and load
257 * statistics from temp table into dbms._column_stats_locked.
259 spi_exec_query("SELECT w.stainherit, w.attname, a.attnum, "
260 "w.nspname_of_typename, tn.nspname, "
261 "w.typname, t.typname, w.atttypmod, a.atttypmod "
262 "FROM pg_catalog.pg_class c "
263 "JOIN pg_catalog.pg_namespace cn "
264 "ON (cn.oid = c.relnamespace) "
265 "JOIN pg_catalog.pg_attribute a "
266 "ON (a.attrelid = c.oid) "
267 "JOIN pg_catalog.pg_type t "
268 "ON (t.oid = a.atttypid) "
269 "JOIN pg_catalog.pg_namespace tn "
270 "ON (tn.oid = t.typnamespace) "
271 "RIGHT JOIN dbms_stats_work_stats w "
272 "ON (w.nspname = cn.nspname AND w.relname = c.relname "
273 "AND (w.attname = a.attname OR w.attname = '')) "
274 "WHERE w.nspname = $1 AND w.relname = $2 "
277 2, r_types, &c_sel_plan, values, NULL, SPI_OK_SELECT);
279 /* This query ought to return at least one record. */
280 if (SPI_processed == 0)
281 elog(ERROR, "no per-column statistic data to be imported");
284 values[2] = w_nspname;
285 values[3] = w_relname;
287 c_num = SPI_processed;
288 c_tups = SPI_tuptable->vals;
289 c_tupdesc = SPI_tuptable->tupdesc;
290 for (j = 0; j < c_num; j++)
292 char *w_typnamespace;
293 char *a_typnamespace;
300 * If we have only per-relation statistics in source, all of
301 * column_stats_effective for per-column statistics are NULL.
303 (void) SPI_getbinval(c_tups[j], c_tupdesc, 1, &isnull);
308 * If there is no column with given name, we skip the rest of
311 values[4] = SPI_getbinval(c_tups[j], c_tupdesc, 2, &isnull);
312 values[1] = SPI_getbinval(c_tups[j], c_tupdesc, 3, &isnull);
315 elog(WARNING, "column \"%s\" of \"%s.%s\" does not exist",
316 DatumGetName(values[4])->data,
317 DatumGetName(w_nspname)->data,
318 DatumGetName(w_relname)->data);
323 * If the destination column has different data type from source
324 * column, we stop importing to avoid corrupted statistics.
326 w_typnamespace = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 4,
328 a_typnamespace = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 5,
330 w_typname = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 6,
332 a_typname = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 7,
334 if (strcmp(w_typnamespace, a_typnamespace) != 0 ||
335 strcmp(w_typname, a_typname) != 0)
338 (errcode(ERRCODE_DATATYPE_MISMATCH),
339 errmsg("column \"%s\" is of type \"%s.%s\""
340 " but import data is of type \"%s.%s\"",
341 DatumGetName(values[4])->data,
342 a_typnamespace, a_typname,
343 w_typnamespace, w_typname)));
348 * If the atttypmod of the destination column is different from the
349 * one of source, column, we stop importing to avoid corrupted
352 w_typmod = DatumGetInt32(SPI_getbinval(c_tups[j], c_tupdesc, 8,
354 a_typmod = DatumGetInt32(SPI_getbinval(c_tups[j], c_tupdesc, 9,
356 if (w_typmod != a_typmod)
359 (errcode(ERRCODE_DATATYPE_MISMATCH),
360 errmsg("column \"%s\" is of atttypmod %d"
361 " but import data is of atttypmod %d",
362 DatumGetName(values[4])->data,
363 a_typmod, a_typmod)));
368 * First delete old dummy statistics, and import new one. We use
369 * DELETE-then-INSERT method here to simplify codes.
371 spi_exec_query("DELETE FROM dbms_stats._column_stats_locked "
372 "WHERE starelid = $1 AND staattnum = $2", 2, c_types,
373 &c_del_plan, values, NULL, SPI_OK_DELETE);
375 spi_exec_query("INSERT INTO dbms_stats._column_stats_locked "
377 "stainherit, stanullfrac, stawidth, stadistinct, "
378 "stakind1, stakind2, stakind3, stakind4, "
379 #if PG_VERSION_NUM >= 90200
382 "staop1, staop2, staop3, staop4, "
383 #if PG_VERSION_NUM >= 90200
386 "stanumbers1, stanumbers2, stanumbers3, stanumbers4, "
387 #if PG_VERSION_NUM >= 90200
390 "stavalues1, stavalues2, stavalues3, stavalues4 "
391 #if PG_VERSION_NUM >= 90200
394 "FROM dbms_stats_work_stats "
395 "WHERE nspname = $3 AND relname = $4 "
398 5, c_types, &c_ins_plan, values, NULL, SPI_OK_INSERT);
400 elog(DEBUG2, "\"%s.%s.%s\" column statistic import",
401 DatumGetName(w_nspname)->data,
402 DatumGetName(w_relname)->data, DatumGetName(values[4])->data);
406 elog(DEBUG2, "\"%s.%s\" column statistic no data",
407 DatumGetName(w_nspname)->data, DatumGetName(w_relname)->data);
410 /* release the cached plan */
411 SPI_freeplan(r_upd_plan);
412 SPI_freeplan(r_ins_plan);
413 SPI_freeplan(c_sel_plan);
414 SPI_freeplan(c_del_plan);
415 SPI_freeplan(c_ins_plan);
417 /* delete the temp table */
418 spi_exec_utility("DROP TABLE dbms_stats_work_stats");
422 if (ret != SPI_OK_FINISH)
423 elog(ERROR, "pg_dbms_stats: SPI_finish => %d", ret);
426 * Recover the protocol state because it has been invalidated by our
429 if (filename == NULL)
430 pq_puttextmessage('C', "dbms_stats_import");
437 * Execute given utility command via SPI.
440 spi_exec_utility(const char *query)
444 ret = SPI_exec(query, 0);
445 if (ret != SPI_OK_UTILITY)
446 elog(ERROR, "pg_dbms_stats: SPI_exec => %d", ret);
451 * Execute given SQL command via SPI.
452 * The plan will be cached by SPI_prepare if it hasn't been.
455 spi_exec_query(const char *query, int nargs, Oid *argtypes, SPIPlanPtr *plan,
456 Datum *values, const char *nulls, int result)
461 *plan = SPI_prepare(query, nargs, argtypes);
463 ret = SPI_execute_plan(*plan, values, nulls, false, 0);
465 elog(ERROR, "pg_dbms_stats: SPI_execute_plan => %d", ret);
469 get_text_arg(FunctionCallInfo fcinfo, int n, bool is_name)
476 arg = PG_GETARG_TEXT_PP(n);
477 s = text_to_cstring(arg);
478 PG_FREE_IF_COPY(arg, n);
485 /* Truncate oversize input */
486 if (len >= NAMEDATALEN)
487 len = pg_mbcliplen(s, len, NAMEDATALEN - 1);
489 /* We use palloc0 here to ensure result is zero-padded */
490 result = (char *) palloc0(NAMEDATALEN);
491 memcpy(result, s, len);
499 * Retrieve arguments from FunctionCallInfo and validate them. We assume
500 * that order of arguments is:
504 * 4) absolute path of source file, or 'stdin' (case insensitive)
507 get_args(FunctionCallInfo fcinfo, char **nspname, char **relname,
508 char **attname, char **filename)
514 Form_pg_class reltup;
517 *nspname = *relname = *attname = *filename = NULL;
520 * First of all, check whether combination of arguments is consistent.
522 * 1) relid and attname can't be used with schemaname.
523 * 2) relid is required when attname is given.
525 if (!PG_ARGISNULL(0) && (!PG_ARGISNULL(1) || !PG_ARGISNULL(2)))
526 elog(ERROR, "relid and attnum can not be used with schemaname");
527 else if (PG_ARGISNULL(1) && !PG_ARGISNULL(2))
528 elog(ERROR, "relation is required");
530 /* filepath validation */
531 if (!PG_ARGISNULL(3))
533 *filename = get_text_arg(fcinfo, 3, false);
536 * If given filepath is "stdin", clear filename to tell caller to
537 * import from standard input. Note that we accept only absolute path
538 * for security reason.
540 if (pg_strcasecmp(*filename, "stdin") == 0)
542 else if (!is_absolute_path(*filename))
544 (errcode(ERRCODE_INVALID_NAME),
545 errmsg("relative path not allowed for dbms_stats_export"
549 /* schemaname validation */
550 if (!PG_ARGISNULL(0))
552 *nspname = get_text_arg(fcinfo, 0, true);
554 /* check that a schema with given name exists */
555 get_namespace_oid(*nspname, false);
557 /* check that given schema is not one of system schemas */
558 if (dbms_stats_is_system_schema_internal(*nspname))
559 elog(ERROR, "\"%s\" is a system catalog", *nspname);
562 /* table oid validation */
563 if (!PG_ARGISNULL(1))
565 relid = PG_GETARG_OID(1);
566 tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
567 if (!HeapTupleIsValid(tp))
568 elog(ERROR, "relid %d does not exist", relid);
570 /* check that the target is an ordinary table or an index */
571 reltup = (Form_pg_class) GETSTRUCT(tp);
572 *relname = pstrdup(reltup->relname.data);
573 relkind = reltup->relkind;
574 nspid = reltup->relnamespace;
577 if (relkind != RELKIND_RELATION && relkind != RELKIND_INDEX
578 #if PG_VERSION_NUM >= 90200
579 && relkind != RELKIND_FOREIGN_TABLE
582 elog(ERROR, "relkind of \"%s\" is \"%c\", can not import",
583 get_rel_name(relid), relkind);
585 /* check that the relation is not in one of system schemas */
586 *nspname = get_namespace_name(nspid);
587 if (dbms_stats_is_system_schema_internal(*nspname))
588 elog(ERROR, "\"%s\" is a system catalog", *nspname);
590 /* attribute name validation */
591 if (!PG_ARGISNULL(2))
593 *attname = get_text_arg(fcinfo, 2, true);
594 attnum = get_attnum(relid, *attname);
595 if (!AttributeNumberIsValid(attnum))
596 elog(ERROR, "column \"%s\" of \"%s.%s\" does not exist", *attname, *nspname, *relname);
602 * appendLiteral - Format a string as a SQL literal, append to buf
604 * This function was copied from simple_quote_literal() in
605 * src/backend/utils/adt/ruleutils.c
608 appendLiteral(StringInfo buf, const char *val)
613 * We form the string literal according to the prevailing setting of
614 * standard_conforming_strings; we never use E''. User is responsible for
615 * making sure result is used correctly.
617 appendStringInfoChar(buf, '\'');
618 for (valptr = val; *valptr; valptr++)
622 if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
623 appendStringInfoChar(buf, ch);
624 appendStringInfoChar(buf, ch);
626 appendStringInfoChar(buf, '\'');
630 * import_stats_from_file
631 * load data from file or stdin into work table, and delete unnecessary
635 import_stats_from_file(char *filename, char *nspname, char *relname,
639 List *parsetree_list;
642 Oid argtypes[3] = { CSTRINGOID, CSTRINGOID, CSTRINGOID };
643 char nulls[3] = { 'n', 'n', 'n' };
648 elog(DEBUG3, "%s() f=%s n=%s r=%s a=%s", __FUNCTION__,
649 filename ? filename : "(null)",
650 nspname ? nspname : "(null)",
651 relname ? relname : "(null)",
652 attname ? attname : "(null)");
655 * Construct COPY statement. NULL for filename indicates that source is
658 initStringInfo(&buf);
659 appendStringInfoString(&buf, "COPY dbms_stats_work_stats FROM ");
660 if (filename == NULL)
661 appendStringInfoString(&buf, "stdin");
663 appendLiteral(&buf, filename);
665 appendStringInfoString(&buf, " (FORMAT 'binary')");
667 /* Execute COPY FROM command. */
668 parsetree_list = pg_parse_query(buf.data);
669 processed = DoCopy((CopyStmt *)linitial(parsetree_list), buf.data);
672 elog(ERROR, "no data to be imported");
675 * Delete the statistics other than the specified object's statistic from
676 * the temp table. We can skip DELETEing staging data when schemaname is
677 * NULL, because it means database-wise import.
682 resetStringInfo(&buf);
683 appendStringInfoString(&buf,
684 "DELETE FROM dbms_stats_work_stats "
685 " WHERE nspname <> $1::text ");
686 values[0] = CStringGetDatum(nspname);
692 values[1] = CStringGetDatum(relname);
695 appendStringInfoString(&buf, " OR (relname <> $2::text) ");
699 values[2] = CStringGetDatum(attname);
702 appendStringInfoString(&buf, " OR (attname <> $3::text) ");
706 ret = SPI_execute_with_args(buf.data, nargs, argtypes, values, nulls,
708 if (ret != SPI_OK_DELETE)
709 elog(ERROR, "pg_dbms_stats: SPI_execute_with_args => %d", ret);
713 void test_import(int *passed, int *total);
714 static void test_spi_exec_query(int *passed, int *total);
715 static void test_spi_exec_utility(int *passed, int *total);
716 static void test_appendLiteral(int *passed, int *total);
718 #define StringEq(actual, expected) \
719 (strcmp((actual), (expected)) == 0 ? 1 : \
720 (elog(WARNING, "%s-%d failed: [%s]", \
721 __FUNCTION__, caseno, (actual)), 0))
724 * Test appendLiteral function
727 test_appendLiteral(int *passed, int *total)
729 bool org_standard_conforming_strings;
733 /* Backup current GUC parameters */
735 org_standard_conforming_strings = standard_conforming_strings;
737 /* Initialize resources for tests */
738 initStringInfo(&buf);
745 resetStringInfo(&buf);
746 appendStringInfoString(&buf, "BEFORE");
747 appendLiteral(&buf, "\"abc 123\tあいう\n\"");
748 if (StringEq(buf.data, "BEFORE'\"abc 123\tあいう\n\"'"))
750 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
756 * - contains special chars (single quote, back slash),
757 * - standard_conforming_strings is true
760 resetStringInfo(&buf);
761 appendStringInfoString(&buf, "BEFORE");
762 standard_conforming_strings = true;
763 appendLiteral(&buf, "'abc 123\tあいう\n\\");
764 if (StringEq(buf.data, "BEFORE'''abc 123\tあいう\n\\'"))
766 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
772 * - contains special chars (single quote, back slash),
773 * - standard_conforming_strings is false
776 resetStringInfo(&buf);
777 appendStringInfoString(&buf, "BEFORE");
778 standard_conforming_strings = false;
779 appendLiteral(&buf, "'abc 123\tあいう\n\\");
780 if (StringEq(buf.data, "BEFORE'''abc 123\tあいう\n\\\\'"))
782 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
791 resetStringInfo(&buf);
792 appendStringInfoString(&buf, "BEFORE");
793 appendLiteral(&buf, "");
794 if (StringEq(buf.data, "BEFORE''"))
796 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
800 /* report # of tests */
803 /* Restore GUC parameters */
804 standard_conforming_strings = org_standard_conforming_strings;
808 test_spi_exec_query(int *passed, int *total)
811 volatile int caseno = 0;
812 SPIPlanPtr ptr = NULL;
817 if (rc != SPI_OK_CONNECT)
818 elog(ERROR, "could not connect SPI: %s", SPI_result_code_string(rc));
822 * - plan is not cached
825 BeginInternalSubTransaction("test");
828 spi_exec_query("SELECT 1", 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
829 if (ptr != NULL && SPI_processed == 1)
831 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
834 ReleaseCurrentSubTransaction();
838 elog(WARNING, "*-*-%d failed", caseno);
839 RollbackAndReleaseCurrentSubTransaction();
840 SPI_restore_connection();
849 BeginInternalSubTransaction("test");
853 spi_exec_query(NULL, 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
854 if (ptr == org_ptr && SPI_processed == 1)
856 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
859 ReleaseCurrentSubTransaction();
863 elog(WARNING, "*-*-%d failed", caseno);
864 RollbackAndReleaseCurrentSubTransaction();
866 SPI_restore_connection();
877 BeginInternalSubTransaction("test");
880 spi_exec_query("SELECT 1 / 0",
881 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
882 elog(WARNING, "*-*-%d failed", caseno);
883 ReleaseCurrentSubTransaction();
887 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
889 RollbackAndReleaseCurrentSubTransaction();
891 SPI_restore_connection();
902 BeginInternalSubTransaction("test");
905 spi_exec_query("SELECT 1", 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
906 if (ptr != NULL && SPI_processed == 1)
908 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
911 ReleaseCurrentSubTransaction();
915 elog(WARNING, "*-*-%d failed", caseno);
917 RollbackAndReleaseCurrentSubTransaction();
918 SPI_restore_connection();
924 /* report # of tests */
929 if (rc != SPI_OK_FINISH && rc != SPI_ERROR_UNCONNECTED)
930 elog(ERROR, "could not finish SPI: %s", SPI_result_code_string(rc));
934 test_spi_exec_utility(int *passed, int *total)
937 volatile int caseno = 0;
941 if (rc != SPI_OK_CONNECT)
942 elog(ERROR, "could not connect SPI: %s", SPI_result_code_string(rc));
949 BeginInternalSubTransaction("test");
952 spi_exec_utility("RESET dummy_parameter");
953 elog(WARNING, "*-*-%d failed", caseno);
954 ReleaseCurrentSubTransaction();
958 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
960 RollbackAndReleaseCurrentSubTransaction();
962 SPI_restore_connection();
971 BeginInternalSubTransaction("test");
974 spi_exec_utility("RESET client_min_messages");
975 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
977 ReleaseCurrentSubTransaction();
981 elog(WARNING, "*-*-%d failed", caseno);
982 RollbackAndReleaseCurrentSubTransaction();
983 SPI_restore_connection();
987 /* report # of tests */
992 if (rc != SPI_OK_FINISH && rc != SPI_ERROR_UNCONNECTED)
993 elog(ERROR, "could not finish SPI: %s", SPI_result_code_string(rc));
997 * Unit test entry point for import.c. This will be called by PG_init()
998 * function, after initialization for this extension is completed .
999 * This funciton should add the numbers of tests passed and the total number of
1000 * tests to parameter passed and total respectively.
1003 test_import(int *passed, int *total)
1005 int local_passed = 0;
1006 int local_total = 0;
1008 elog(WARNING, "==========");
1011 test_appendLiteral(&local_passed, &local_total);
1012 test_spi_exec_query(&local_passed, &local_total);
1013 test_spi_exec_utility(&local_passed, &local_total);
1015 elog(WARNING, "%s %d/%d passed", __FUNCTION__, local_passed, local_total);
1016 *passed += local_passed;
1017 *total += local_total;