This file is indexed.

/usr/share/doc/collectd-core/examples/postgresql/collectd_insert.sql is in collectd-core 5.4.1-6+deb8u1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
-- collectd - contrib/postgresql/collectd_insert.sql
-- Copyright (C) 2012 Sebastian 'tokkee' Harl
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
--
-- - Redistributions of source code must retain the above copyright
--   notice, this list of conditions and the following disclaimer.
--
-- - Redistributions in binary form must reproduce the above copyright
--   notice, this list of conditions and the following disclaimer in the
--   documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.

-- Description:
-- ------------
--
-- This is a sample database setup that may be used to write data collected by
-- collectd to a PostgreSQL database. We're using two tables, 'identifiers'
-- and 'values' to store the value-list identifier and the actual values
-- respectively.
--
-- The 'values' table is partitioned to improve performance and maintainance.
-- Please note that additional maintainance scripts are required in order to
-- keep the setup running -- see the comments below for details.
--
-- The function 'collectd_insert' may be used to actually insert values
-- submitted by collectd into those tables.
--
-- Sample configuration:
-- ---------------------
--
-- <Plugin postgresql>
--     <Writer sqlstore>
--         Statement "SELECT collectd_insert($1, $2, $3, $4, $5, $6, $7, $8, $9);"
--     </Writer>
--     <Database foo>
--         # ...
--         Writer sqlstore
--     </Database>
-- </Plugin>

CREATE TABLE identifiers (
    id integer NOT NULL,
    host character varying(64) NOT NULL,
    plugin character varying(64) NOT NULL,
    plugin_inst character varying(64) DEFAULT NULL::character varying,
    type character varying(64) NOT NULL,
    type_inst character varying(64) DEFAULT NULL::character varying
);
CREATE SEQUENCE identifiers_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER SEQUENCE identifiers_id_seq OWNED BY identifiers.id;
ALTER TABLE ONLY identifiers
    ALTER COLUMN id SET DEFAULT nextval('identifiers_id_seq'::regclass);
ALTER TABLE ONLY identifiers
    ADD CONSTRAINT identifiers_host_plugin_plugin_inst_type_type_inst_key
        UNIQUE (host, plugin, plugin_inst, type, type_inst);
ALTER TABLE ONLY identifiers
    ADD CONSTRAINT identifiers_pkey PRIMARY KEY (id);

-- optionally, create indexes for the identifier fields
CREATE INDEX identifiers_host ON identifiers USING btree (host);
CREATE INDEX identifiers_plugin ON identifiers USING btree (plugin);
CREATE INDEX identifiers_plugin_inst ON identifiers USING btree (plugin_inst);
CREATE INDEX identifiers_type ON identifiers USING btree (type);
CREATE INDEX identifiers_type_inst ON identifiers USING btree (type_inst);

CREATE TABLE "values" (
    id integer NOT NULL,
    tstamp timestamp with time zone NOT NULL,
    name character varying(64) NOT NULL,
    value double precision NOT NULL
);

CREATE OR REPLACE VIEW collectd
    AS SELECT host, plugin, plugin_inst, type, type_inst,
            host
                || '/' || plugin
                || CASE
                    WHEN plugin_inst IS NOT NULL THEN '-'
                    ELSE ''
                END
                || coalesce(plugin_inst, '')
                || '/' || type
                || CASE
                    WHEN type_inst IS NOT NULL THEN '-'
                    ELSE ''
                END
                || coalesce(type_inst, '') AS identifier,
            tstamp, name, value
        FROM identifiers
            JOIN values
            ON values.id = identifiers.id;

-- partition "values" by day (or week, month, ...)

-- create the child tables for today and the next 'days' days:
-- this may, for example, be used in a daily cron-job (or similar) to create
-- the tables for the next couple of days
CREATE OR REPLACE FUNCTION values_update_childs(
        integer
    ) RETURNS SETOF text
    LANGUAGE plpgsql
    AS $_$
DECLARE
    days alias for $1;
    cur_day date;
    next_day date;
    i integer;
BEGIN
    IF days < 1 THEN
        RAISE EXCEPTION 'Cannot have negative number of days';
    END IF;

    i := 0;
    LOOP
        EXIT WHEN i > days;

        SELECT CAST ('now'::date + i * '1day'::interval AS date) INTO cur_day;
        SELECT CAST ('now'::date + (i + 1) * '1day'::interval AS date) INTO next_day;

        i := i + 1;

        BEGIN
            EXECUTE 'CREATE TABLE "values$' || cur_day || '" (
                CHECK (tstamp >= TIMESTAMP ''' || cur_day || ''' '
                    || 'AND tstamp < TIMESTAMP ''' || next_day || ''')
            ) INHERITS (values)';
        EXCEPTION WHEN duplicate_table THEN
            CONTINUE;
        END;

        RETURN NEXT 'values$' || cur_day::text;

        EXECUTE 'ALTER TABLE ONLY "values$' || cur_day || '"
            ADD CONSTRAINT "values_' || cur_day || '_pkey"
                PRIMARY KEY (id, tstamp, name, value)';
        EXECUTE 'ALTER TABLE ONLY "values$' || cur_day || '"
            ADD CONSTRAINT "values_' || cur_day || '_id_fkey"
                FOREIGN KEY (id) REFERENCES identifiers(id)';
    END LOOP;
    RETURN;
END;
$_$;

-- create initial child tables
SELECT values_update_childs(2);

CREATE OR REPLACE FUNCTION values_insert_trigger()
    RETURNS trigger
    LANGUAGE plpgsql
    AS $_$
DECLARE
    child_tbl character varying;
BEGIN
    SELECT 'values$' || CAST (NEW.tstamp AS DATE) INTO child_tbl;
    -- Rather than using 'EXECUTE', some if-cascade checking the date may also
    -- be used. However, this would require frequent updates of the trigger
    -- function while this example works automatically.
    EXECUTE 'INSERT INTO "' || child_tbl || '" VALUES ($1.*)' USING NEW;
    RETURN NULL;
END;
$_$;

CREATE TRIGGER insert_values_trigger
    BEFORE INSERT ON values
    FOR EACH ROW EXECUTE PROCEDURE values_insert_trigger();

-- when querying values make sure to enable constraint exclusion
-- SET constraint_exclusion = on;

CREATE OR REPLACE FUNCTION collectd_insert(
        timestamp with time zone, character varying,
        character varying, character varying,
        character varying, character varying,
        character varying[], character varying[], double precision[]
    ) RETURNS void
    LANGUAGE plpgsql
    AS $_$
DECLARE
    p_time alias for $1;
    p_host alias for $2;
    p_plugin alias for $3;
    p_plugin_instance alias for $4;
    p_type alias for $5;
    p_type_instance alias for $6;
    p_value_names alias for $7;
    -- don't use the type info; for 'StoreRates true' it's 'gauge' anyway
    -- p_type_names alias for $8;
    p_values alias for $9;
    ds_id integer;
    i integer;
BEGIN
    SELECT id INTO ds_id
        FROM identifiers
        WHERE host = p_host
            AND plugin = p_plugin
            AND COALESCE(plugin_inst, '') = COALESCE(p_plugin_instance, '')
            AND type = p_type
            AND COALESCE(type_inst, '') = COALESCE(p_type_instance, '');
    IF NOT FOUND THEN
        INSERT INTO identifiers (host, plugin, plugin_inst, type, type_inst)
            VALUES (p_host, p_plugin, p_plugin_instance, p_type, p_type_instance)
            RETURNING id INTO ds_id;
    END IF;
    i := 1;
    LOOP
        EXIT WHEN i > array_upper(p_value_names, 1);
        INSERT INTO values (id, tstamp, name, value)
            VALUES (ds_id, p_time, p_value_names[i], p_values[i]);
        i := i + 1;
    END LOOP;
END;
$_$;

-- vim: set expandtab :