4 * Copyright (c) 2012-2017, 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 #include "catalog/pg_class.h"
20 #if PG_VERSION_NUM >= 90300
21 #include "access/htup_details.h"
24 #include "pg_dbms_stats.h"
26 #define RELATION_PARAM_NUM 9
28 extern PGDLLIMPORT bool standard_conforming_strings;
30 PG_FUNCTION_INFO_V1(dbms_stats_import);
32 Datum dbms_stats_import(PG_FUNCTION_ARGS);
34 static void get_args(FunctionCallInfo fcinfo, char **nspname, char **relname,
35 char **attname, char **filename);
36 static void spi_exec_utility(const char *query);
37 static void spi_exec_query(const char *query, int nargs, Oid *argtypes,
38 SPIPlanPtr *plan, Datum *values, const char *nulls, int result);
39 static void import_stats_from_file(char *filename, char *nspname, char *relname,
44 * Import exported statistics from stdin or a file.
50 * 4) absolute path of source file, or 'stdin' (case insensitive)
53 dbms_stats_import(PG_FUNCTION_ARGS)
58 char *filename; /* filename, or NULL for STDIN */
64 SPIPlanPtr r_upd_plan = NULL;
65 SPIPlanPtr r_ins_plan = NULL;
66 SPIPlanPtr c_sel_plan = NULL;
67 SPIPlanPtr c_del_plan = NULL;
68 SPIPlanPtr c_ins_plan = NULL;
70 /* get validated arguments */
71 get_args(fcinfo, &nspname, &relname, &attname, &filename);
74 elog(DEBUG3, "%s() f=%s n=%s r=%s a=%s", __FUNCTION__,
75 filename ? filename : "(null)",
76 nspname ? nspname : "(null)",
77 relname ? relname : "(null)",
78 attname ? attname : "(null)");
82 if (ret != SPI_OK_CONNECT)
83 elog(ERROR, "pg_dbms_stats: SPI_connect => %d", ret);
85 /* lock dummy statistics tables. */
86 spi_exec_utility("LOCK dbms_stats.relation_stats_locked"
87 " IN SHARE UPDATE EXCLUSIVE MODE");
88 spi_exec_utility("LOCK dbms_stats.column_stats_locked"
89 " IN SHARE UPDATE EXCLUSIVE MODE");
92 * Create a temp table to save the statistics to import.
93 * This table should fit with the content of export files.
95 spi_exec_utility("CREATE TEMP TABLE dbms_stats_work_stats ("
96 "nspname name NOT NULL,"
97 "relname name NOT NULL,"
98 "relpages int4 NOT NULL,"
99 "reltuples float4 NOT NULL,"
100 "relallvisible int4 NOT NULL,"
101 "curpages int4 NOT NULL,"
102 "last_analyze timestamp with time zone,"
103 "last_autoanalyze timestamp with time zone,"
105 "nspname_of_typename name,"
109 "stanullfrac float4,"
111 "stadistinct float4,"
122 "stanumbers1 float4[],"
123 "stanumbers2 float4[],"
124 "stanumbers3 float4[],"
125 "stanumbers4 float4[],"
126 "stanumbers5 float4[],"
127 "stavalues1 dbms_stats.anyarray,"
128 "stavalues2 dbms_stats.anyarray,"
129 "stavalues3 dbms_stats.anyarray,"
130 "stavalues4 dbms_stats.anyarray"
131 ",stavalues5 dbms_stats.anyarray"
134 /* load the statistics from export file to the temp table */
135 import_stats_from_file(filename, nspname, relname, attname);
137 /* Determine the Oid of local table from the tablename and schemaname. */
138 ret = SPI_execute("SELECT DISTINCT w.nspname, w.relname, c.oid, "
139 "w.relpages, w.reltuples, "
140 "w.curpages, w.last_analyze, w.last_autoanalyze "
142 "FROM pg_catalog.pg_class c "
143 "JOIN pg_catalog.pg_namespace n "
144 "ON (c.relnamespace = n.oid) "
145 "RIGHT JOIN dbms_stats_work_stats w "
146 "ON (w.relname = c.relname AND w.nspname = n.nspname) "
147 "ORDER BY 1, 2", false, 0);
148 if (ret != SPI_OK_SELECT)
149 elog(ERROR, "pg_dbms_stats: SPI_execute => %d", ret);
152 * If there is no record in the staging table after loading source and
153 * deleting unnecessary records, we treat it as an error.
155 if (SPI_processed == 0)
156 elog(ERROR, "no per-table statistic data to be imported");
159 r_num = SPI_processed;
160 r_tups = SPI_tuptable->vals;
161 r_tupdesc = SPI_tuptable->tupdesc;
162 for (i = 0; i < r_num; i++)
169 char nulls[9] = {'t', 't', 't', 't', 't', 't', 't', 't', 't'};
170 Oid r_types[9] = {NAMEOID, NAMEOID, INT4OID, FLOAT4OID, INT4OID,
171 TIMESTAMPTZOID, TIMESTAMPTZOID, OIDOID, INT4OID};
172 Oid c_types[5] = {OIDOID, INT2OID, NAMEOID, NAMEOID,
179 values[0] = w_nspname = SPI_getbinval(r_tups[i], r_tupdesc, 1, &isnull);
180 values[1] = w_relname = SPI_getbinval(r_tups[i], r_tupdesc, 2, &isnull);
181 values[7] = w_relid = SPI_getbinval(r_tups[i], r_tupdesc, 3, &isnull);
184 elog(WARNING, "relation \"%s.%s\" does not exist",
185 DatumGetName(w_nspname)->data,
186 DatumGetName(w_relname)->data);
190 values[2] = SPI_getbinval(r_tups[i], r_tupdesc, 4, &isnull);
191 values[3] = SPI_getbinval(r_tups[i], r_tupdesc, 5, &isnull);
192 values[4] = SPI_getbinval(r_tups[i], r_tupdesc, 6, &isnull);
193 values[5] = SPI_getbinval(r_tups[i], r_tupdesc, 7, &isnull);
194 nulls[5] = isnull ? 'n' : 't';
195 values[6] = SPI_getbinval(r_tups[i], r_tupdesc, 8, &isnull);
196 nulls[6] = isnull ? 'n' : 't';
197 values[8] = SPI_getbinval(r_tups[i], r_tupdesc, 9, &isnull);
200 * First we try UPDATE with the oid. When no record matched, try
201 * INSERT. We can't use DELETE-then-INSERT method because we have FK
202 * on relation_stats_locked so DELETE would delete child records in
203 * column_stats_locked undesirably.
205 spi_exec_query("UPDATE dbms_stats.relation_stats_locked SET "
206 "relname = quote_ident($1) || '.' || quote_ident($2), "
207 "relpages = $3, reltuples = $4, relallvisible = $9, "
208 "curpages = $5, last_analyze = $6, last_autoanalyze = $7 "
210 RELATION_PARAM_NUM, r_types, &r_upd_plan, values, nulls,
212 if (SPI_processed == 0)
214 spi_exec_query("INSERT INTO dbms_stats.relation_stats_locked "
215 "(relname, relpages, reltuples, curpages, "
216 "last_analyze, last_autoanalyze, relid, relallvisible"
217 ") VALUES (quote_ident($1) || '.' || quote_ident($2), "
218 "$3, $4, $5, $6, $7, $8, $9)",
219 RELATION_PARAM_NUM, r_types, &r_ins_plan, values, nulls,
221 /* If we failed to insert, we can't proceed. */
222 if (SPI_processed != 1)
223 elog(ERROR, "failed to insert import data");
226 elog(DEBUG2, "\"%s.%s\" relation statistic import",
227 DatumGetName(w_nspname)->data, DatumGetName(w_relname)->data);
230 * Determine the attnum of the attribute with given name, and load
231 * statistics from temp table into dbms.column_stats_locked.
233 spi_exec_query("SELECT w.stainherit, w.attname, a.attnum, "
234 "w.nspname_of_typename, tn.nspname, "
235 "w.typname, t.typname, w.atttypmod, a.atttypmod "
236 "FROM pg_catalog.pg_class c "
237 "JOIN pg_catalog.pg_namespace cn "
238 "ON (cn.oid = c.relnamespace) "
239 "JOIN pg_catalog.pg_attribute a "
240 "ON (a.attrelid = c.oid) "
241 "JOIN pg_catalog.pg_type t "
242 "ON (t.oid = a.atttypid) "
243 "JOIN pg_catalog.pg_namespace tn "
244 "ON (tn.oid = t.typnamespace) "
245 "RIGHT JOIN dbms_stats_work_stats w "
246 "ON (w.nspname = cn.nspname AND w.relname = c.relname "
247 "AND (w.attname = a.attname OR w.attname = '')) "
248 "WHERE w.nspname = $1 AND w.relname = $2 "
251 2, r_types, &c_sel_plan, values, NULL, SPI_OK_SELECT);
253 /* This query ought to return at least one record. */
254 if (SPI_processed == 0)
255 elog(ERROR, "no per-column statistic data to be imported");
258 values[2] = w_nspname;
259 values[3] = w_relname;
261 c_num = SPI_processed;
262 c_tups = SPI_tuptable->vals;
263 c_tupdesc = SPI_tuptable->tupdesc;
264 for (j = 0; j < c_num; j++)
266 char *w_typnamespace;
267 char *a_typnamespace;
274 * If we have only per-relation statistics in source, all of
275 * column_stats_effective for per-column statistics are NULL.
277 (void) SPI_getbinval(c_tups[j], c_tupdesc, 1, &isnull);
282 * If there is no column with given name, we skip the rest of
285 values[4] = SPI_getbinval(c_tups[j], c_tupdesc, 2, &isnull);
286 values[1] = SPI_getbinval(c_tups[j], c_tupdesc, 3, &isnull);
289 elog(WARNING, "column \"%s\" of \"%s.%s\" does not exist",
290 DatumGetName(values[4])->data,
291 DatumGetName(w_nspname)->data,
292 DatumGetName(w_relname)->data);
297 * If the destination column has different data type from source
298 * column, we stop importing to avoid corrupted statistics.
300 w_typnamespace = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 4,
302 a_typnamespace = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 5,
304 w_typname = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 6,
306 a_typname = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 7,
308 if (strcmp(w_typnamespace, a_typnamespace) != 0 ||
309 strcmp(w_typname, a_typname) != 0)
312 (errcode(ERRCODE_DATATYPE_MISMATCH),
313 errmsg("column \"%s\" is of type \"%s.%s\""
314 " but import data is of type \"%s.%s\"",
315 DatumGetName(values[4])->data,
316 a_typnamespace, a_typname,
317 w_typnamespace, w_typname)));
322 * If the atttypmod of the destination column is different from the
323 * one of source, column, we stop importing to avoid corrupted
326 w_typmod = DatumGetInt32(SPI_getbinval(c_tups[j], c_tupdesc, 8,
328 a_typmod = DatumGetInt32(SPI_getbinval(c_tups[j], c_tupdesc, 9,
330 if (w_typmod != a_typmod)
333 (errcode(ERRCODE_DATATYPE_MISMATCH),
334 errmsg("column \"%s\" is of atttypmod %d"
335 " but import data is of atttypmod %d",
336 DatumGetName(values[4])->data,
337 a_typmod, a_typmod)));
342 * First delete old dummy statistics, and import new one. We use
343 * DELETE-then-INSERT method here to simplify codes.
345 spi_exec_query("DELETE FROM dbms_stats.column_stats_locked "
346 "WHERE starelid = $1 AND staattnum = $2", 2, c_types,
347 &c_del_plan, values, NULL, SPI_OK_DELETE);
349 spi_exec_query("INSERT INTO dbms_stats.column_stats_locked "
351 "stainherit, stanullfrac, stawidth, stadistinct, "
352 "stakind1, stakind2, stakind3, stakind4, stakind5, "
353 "staop1, staop2, staop3, staop4, staop5, "
354 "stanumbers1, stanumbers2, stanumbers3, stanumbers4, "
356 "stavalues1, stavalues2, stavalues3, stavalues4 , stavalues5 "
357 "FROM dbms_stats_work_stats "
358 "WHERE nspname = $3 AND relname = $4 "
361 5, c_types, &c_ins_plan, values, NULL, SPI_OK_INSERT);
363 elog(DEBUG2, "\"%s.%s.%s\" column statistic import",
364 DatumGetName(w_nspname)->data,
365 DatumGetName(w_relname)->data, DatumGetName(values[4])->data);
369 elog(DEBUG2, "\"%s.%s\" column statistic no data",
370 DatumGetName(w_nspname)->data, DatumGetName(w_relname)->data);
373 /* release the cached plan */
374 SPI_freeplan(r_upd_plan);
375 SPI_freeplan(r_ins_plan);
376 SPI_freeplan(c_sel_plan);
377 SPI_freeplan(c_del_plan);
378 SPI_freeplan(c_ins_plan);
380 /* delete the temp table */
381 spi_exec_utility("DROP TABLE dbms_stats_work_stats");
385 if (ret != SPI_OK_FINISH)
386 elog(ERROR, "pg_dbms_stats: SPI_finish => %d", ret);
389 * Recover the protocol state because it has been invalidated by our
392 if (filename == NULL)
393 pq_puttextmessage('C', "dbms_stats_import");
400 * Execute given utility command via SPI.
403 spi_exec_utility(const char *query)
407 ret = SPI_exec(query, 0);
408 if (ret != SPI_OK_UTILITY)
409 elog(ERROR, "pg_dbms_stats: SPI_exec => %d", ret);
414 * Execute given SQL command via SPI.
415 * The plan will be cached by SPI_prepare if it hasn't been.
418 spi_exec_query(const char *query, int nargs, Oid *argtypes, SPIPlanPtr *plan,
419 Datum *values, const char *nulls, int result)
424 *plan = SPI_prepare(query, nargs, argtypes);
426 ret = SPI_execute_plan(*plan, values, nulls, false, 0);
428 elog(ERROR, "pg_dbms_stats: SPI_execute_plan => %d", ret);
432 get_text_arg(FunctionCallInfo fcinfo, int n, bool is_name)
439 arg = PG_GETARG_TEXT_PP(n);
440 s = text_to_cstring(arg);
441 PG_FREE_IF_COPY(arg, n);
448 /* Truncate oversize input */
449 if (len >= NAMEDATALEN)
450 len = pg_mbcliplen(s, len, NAMEDATALEN - 1);
452 /* We use palloc0 here to ensure result is zero-padded */
453 result = (char *) palloc0(NAMEDATALEN);
454 memcpy(result, s, len);
462 * Retrieve arguments from FunctionCallInfo and validate them. We assume
463 * that order of arguments is:
467 * 4) absolute path of source file, or 'stdin' (case insensitive)
470 get_args(FunctionCallInfo fcinfo, char **nspname, char **relname,
471 char **attname, char **filename)
477 Form_pg_class reltup;
480 *nspname = *relname = *attname = *filename = NULL;
483 * First of all, check whether combination of arguments is consistent.
485 * 1) relid and attname can't be used with schemaname.
486 * 2) relid is required when attname is given.
488 if (!PG_ARGISNULL(0) && (!PG_ARGISNULL(1) || !PG_ARGISNULL(2)))
489 elog(ERROR, "relid and attnum can not be used with schemaname");
490 else if (PG_ARGISNULL(1) && !PG_ARGISNULL(2))
491 elog(ERROR, "relation is required");
493 /* filepath validation */
494 if (!PG_ARGISNULL(3))
496 *filename = get_text_arg(fcinfo, 3, false);
499 * If given filepath is "stdin", clear filename to tell caller to
500 * import from standard input. Note that we accept only absolute path
501 * for security reason.
503 if (pg_strcasecmp(*filename, "stdin") == 0)
505 else if (!is_absolute_path(*filename))
507 (errcode(ERRCODE_INVALID_NAME),
508 errmsg("relative path not allowed for dbms_stats_export"
512 /* schemaname validation */
513 if (!PG_ARGISNULL(0))
515 *nspname = get_text_arg(fcinfo, 0, true);
517 /* check that a schema with given name exists */
518 get_namespace_oid(*nspname, false);
520 /* check that given schema is not one of system schemas */
521 if (dbms_stats_is_system_schema_internal(*nspname))
522 elog(ERROR, "\"%s\" is a system catalog", *nspname);
525 /* table oid validation */
526 if (!PG_ARGISNULL(1))
528 relid = PG_GETARG_OID(1);
529 tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
530 if (!HeapTupleIsValid(tp))
531 elog(ERROR, "relid %d does not exist", relid);
533 /* check that the target is an ordinary table or an index */
534 reltup = (Form_pg_class) GETSTRUCT(tp);
535 *relname = pstrdup(reltup->relname.data);
536 relkind = reltup->relkind;
537 nspid = reltup->relnamespace;
540 if (relkind != RELKIND_RELATION && relkind != RELKIND_INDEX
541 && relkind != RELKIND_FOREIGN_TABLE
542 #if PG_VERSION_NUM >= 90300
543 && relkind != RELKIND_MATVIEW
546 elog(ERROR, "relkind of \"%s\" is \"%c\", can not import",
547 get_rel_name(relid), relkind);
549 /* check that the relation is not in one of system schemas */
550 *nspname = get_namespace_name(nspid);
551 if (dbms_stats_is_system_schema_internal(*nspname))
552 elog(ERROR, "\"%s\" is a system catalog", *nspname);
554 /* attribute name validation */
555 if (!PG_ARGISNULL(2))
557 *attname = get_text_arg(fcinfo, 2, true);
558 attnum = get_attnum(relid, *attname);
559 if (!AttributeNumberIsValid(attnum))
560 elog(ERROR, "column \"%s\" of \"%s.%s\" does not exist", *attname, *nspname, *relname);
566 * appendLiteral - Format a string as a SQL literal, append to buf
568 * This function was copied from simple_quote_literal() in
569 * src/backend/utils/adt/ruleutils.c
572 appendLiteral(StringInfo buf, const char *val)
577 * We form the string literal according to the prevailing setting of
578 * standard_conforming_strings; we never use E''. User is responsible for
579 * making sure result is used correctly.
581 appendStringInfoChar(buf, '\'');
582 for (valptr = val; *valptr; valptr++)
586 if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
587 appendStringInfoChar(buf, ch);
588 appendStringInfoChar(buf, ch);
590 appendStringInfoChar(buf, '\'');
594 * import_stats_from_file
595 * load data from file or stdin into work table, and delete unnecessary
599 import_stats_from_file(char *filename, char *nspname, char *relname,
603 List *parsetree_list;
606 Oid argtypes[3] = { CSTRINGOID, CSTRINGOID, CSTRINGOID };
607 char nulls[3] = { 'n', 'n', 'n' };
612 elog(DEBUG3, "%s() f=%s n=%s r=%s a=%s", __FUNCTION__,
613 filename ? filename : "(null)",
614 nspname ? nspname : "(null)",
615 relname ? relname : "(null)",
616 attname ? attname : "(null)");
619 * Construct COPY statement. NULL for filename indicates that source is
622 initStringInfo(&buf);
623 appendStringInfoString(&buf, "COPY dbms_stats_work_stats FROM ");
624 if (filename == NULL)
625 appendStringInfoString(&buf, "stdin");
627 appendLiteral(&buf, filename);
629 appendStringInfoString(&buf, " (FORMAT 'binary')");
631 /* Execute COPY FROM command. */
632 parsetree_list = pg_parse_query(buf.data);
634 #if PG_VERSION_NUM >= 100000
637 * parsetree_list is a list with one RawStmt since Pg10. Extract
638 * CopyStmt to feed to DoCopy.
640 ParseState *pstate = make_parsestate(NULL);
641 RawStmt *rstmt = (RawStmt *)linitial (parsetree_list);
642 CopyStmt *stmt = (CopyStmt *)rstmt->stmt;
644 Assert(IsA(stmt, CopyStmt));
646 pstate->p_sourcetext = pstrdup(buf.data);
647 DoCopy(pstate, stmt, rstmt->stmt_location, rstmt->stmt_len, &processed);
648 free_parsestate(pstate);
650 #elif PG_VERSION_NUM >= 90300
651 DoCopy((CopyStmt *)linitial(parsetree_list), buf.data, &processed);
653 processed = DoCopy((CopyStmt *)linitial(parsetree_list), buf.data);
657 elog(ERROR, "no data to be imported");
660 * Delete the statistics other than the specified object's statistic from
661 * the temp table. We can skip DELETEing staging data when schemaname is
662 * NULL, because it means database-wise import.
667 resetStringInfo(&buf);
668 appendStringInfoString(&buf,
669 "DELETE FROM dbms_stats_work_stats "
670 " WHERE nspname <> $1::text ");
671 values[0] = CStringGetDatum(nspname);
677 values[1] = CStringGetDatum(relname);
680 appendStringInfoString(&buf, " OR (relname <> $2::text) ");
684 values[2] = CStringGetDatum(attname);
687 appendStringInfoString(&buf, " OR (attname <> $3::text) ");
691 ret = SPI_execute_with_args(buf.data, nargs, argtypes, values, nulls,
693 if (ret != SPI_OK_DELETE)
694 elog(ERROR, "pg_dbms_stats: SPI_execute_with_args => %d", ret);
698 void test_import(int *passed, int *total);
699 static void test_spi_exec_query(int *passed, int *total);
700 static void test_spi_exec_utility(int *passed, int *total);
701 static void test_appendLiteral(int *passed, int *total);
703 #define StringEq(actual, expected) \
704 (strcmp((actual), (expected)) == 0 ? 1 : 0)
707 * Test appendLiteral function
710 test_appendLiteral(int *passed, int *total)
712 bool org_standard_conforming_strings;
716 /* Backup current GUC parameters */
718 org_standard_conforming_strings = standard_conforming_strings;
720 /* Initialize resources for tests */
721 initStringInfo(&buf);
728 resetStringInfo(&buf);
729 appendStringInfoString(&buf, "BEFORE");
730 appendLiteral(&buf, "\"abc 123\tあいう\n\"");
731 if (StringEq(buf.data, "BEFORE'\"abc 123\tあいう\n\"'"))
733 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
738 elog(WARNING, "%s-%d failed: [%s]", __FUNCTION__, caseno, buf.data);
743 * - contains special chars (single quote, back slash),
744 * - standard_conforming_strings is true
747 resetStringInfo(&buf);
748 appendStringInfoString(&buf, "BEFORE");
749 standard_conforming_strings = true;
750 appendLiteral(&buf, "'abc 123\tあいう\n\\");
751 if (StringEq(buf.data, "BEFORE'''abc 123\tあいう\n\\'"))
753 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
758 elog(WARNING, "%s-%d failed: [%s]", __FUNCTION__, caseno, buf.data);
763 * - contains special chars (single quote, back slash),
764 * - standard_conforming_strings is false
767 resetStringInfo(&buf);
768 appendStringInfoString(&buf, "BEFORE");
769 standard_conforming_strings = false;
770 appendLiteral(&buf, "'abc 123\tあいう\n\\");
771 if (StringEq(buf.data, "BEFORE'''abc 123\tあいう\n\\\\'"))
773 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
778 elog(WARNING, "%s-%d failed: [%s]", __FUNCTION__, caseno, buf.data);
786 resetStringInfo(&buf);
787 appendStringInfoString(&buf, "BEFORE");
788 appendLiteral(&buf, "");
789 if (StringEq(buf.data, "BEFORE''"))
791 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
796 elog(WARNING, "%s-%d failed: [%s]", __FUNCTION__, caseno, buf.data);
799 /* report # of tests */
802 /* Restore GUC parameters */
803 standard_conforming_strings = org_standard_conforming_strings;
807 test_spi_exec_query(int *passed, int *total)
810 volatile int caseno = 0;
811 SPIPlanPtr ptr = NULL;
816 if (rc != SPI_OK_CONNECT)
817 elog(ERROR, "could not connect SPI: %s", SPI_result_code_string(rc));
821 * - plan is not cached
824 BeginInternalSubTransaction("test");
827 spi_exec_query("SELECT 1", 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
828 if (ptr != NULL && SPI_processed == 1)
830 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
833 ReleaseCurrentSubTransaction();
837 elog(WARNING, "*-*-%d failed", caseno);
838 RollbackAndReleaseCurrentSubTransaction();
839 SPI_restore_connection();
848 BeginInternalSubTransaction("test");
852 spi_exec_query(NULL, 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
853 if (ptr == org_ptr && SPI_processed == 1)
855 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
858 ReleaseCurrentSubTransaction();
862 elog(WARNING, "*-*-%d failed", caseno);
863 RollbackAndReleaseCurrentSubTransaction();
865 SPI_restore_connection();
876 BeginInternalSubTransaction("test");
879 spi_exec_query("SELECT 1 / 0",
880 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
881 elog(WARNING, "*-*-%d failed", caseno);
882 ReleaseCurrentSubTransaction();
886 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
888 RollbackAndReleaseCurrentSubTransaction();
890 SPI_restore_connection();
901 BeginInternalSubTransaction("test");
904 spi_exec_query("SELECT 1", 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT);
905 if (ptr != NULL && SPI_processed == 1)
907 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
910 ReleaseCurrentSubTransaction();
914 elog(WARNING, "*-*-%d failed", caseno);
916 RollbackAndReleaseCurrentSubTransaction();
917 SPI_restore_connection();
923 /* report # of tests */
928 if (rc != SPI_OK_FINISH && rc != SPI_ERROR_UNCONNECTED)
929 elog(ERROR, "could not finish SPI: %s", SPI_result_code_string(rc));
933 test_spi_exec_utility(int *passed, int *total)
936 volatile int caseno = 0;
940 if (rc != SPI_OK_CONNECT)
941 elog(ERROR, "could not connect SPI: %s", SPI_result_code_string(rc));
948 BeginInternalSubTransaction("test");
951 spi_exec_utility("RESET dummy_parameter");
952 elog(WARNING, "*-*-%d failed", caseno);
953 ReleaseCurrentSubTransaction();
957 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
959 RollbackAndReleaseCurrentSubTransaction();
961 SPI_restore_connection();
970 BeginInternalSubTransaction("test");
973 spi_exec_utility("RESET client_min_messages");
974 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
976 ReleaseCurrentSubTransaction();
980 elog(WARNING, "*-*-%d failed", caseno);
981 RollbackAndReleaseCurrentSubTransaction();
982 SPI_restore_connection();
986 /* report # of tests */
991 if (rc != SPI_OK_FINISH && rc != SPI_ERROR_UNCONNECTED)
992 elog(ERROR, "could not finish SPI: %s", SPI_result_code_string(rc));
996 * Unit test entry point for import.c. This will be called by PG_init()
997 * function, after initialization for this extension is completed .
998 * This funciton should add the numbers of tests passed and the total number of
999 * tests to parameter passed and total respectively.
1002 test_import(int *passed, int *total)
1004 int local_passed = 0;
1005 int local_total = 0;
1007 elog(WARNING, "==========");
1010 test_appendLiteral(&local_passed, &local_total);
1011 test_spi_exec_query(&local_passed, &local_total);
1012 test_spi_exec_utility(&local_passed, &local_total);
1014 elog(WARNING, "%s %d/%d passed", __FUNCTION__, local_passed, local_total);
1015 *passed += local_passed;
1016 *total += local_total;