Hoi,
Voor een project ben ik bezig om een dynamische database te maken.
Wat houdt dit in?
De gebruiker moet on-the-fly onderwerpen (Tabellen) aan kunnen maken
Deze tabellen moeten zelf in te delen zijn.
Hiervoor heb ik de volgende aanpak gedaan:
Er is een hoofdtabel (in dit geval: ITO) waar alle metadata in staat:
met nog een aantal FK's e.d.
De metadata van de velden in de dynamische tabellen worden ook opgeslagen, verrassend in de tabel: ito_fields:
Waarbij fieldtype verwijst naar een native datatype.
Als deze 2 tabellen zijn gevuld voor een onderwerp, wordt de dynamische tabel gecreëerd. dit werkt als een tierelier.
Maar dan:
De users hebben geen toegang op de tabellen zelf. Kortom, via functions wordt de data ontsloten. Hier komt mijn probleem:
Vanuit een functie moet er data toegevoegd worden aan een dynamische tabel.
Omdat je niet weet hoeveel kolommen er in een tabel zitten, is er hier gekozen voor een array. Om toch alle datatypes te ondersteunen, is er gekozen voor een array of text (text[]).
Voor integers, booleans en double precisions is dit geen probleem, maar bij het invoegen van text gaat het mis.
Omdat de query aan elkaar geconcatenate wordt, gaat het mis bij het toevoegen van de (single) quotes. Postgres vertikt het om te begrijpen dat het geëscapede quotes zijn. Zelfs bij een constructie als geescapede quotes gaat het nog mis.
Ook bracht de quote_literal/quote_nullable geen uitkomst.
Zie ik hier iets over het hoofd, is er een makkelijkere oplossing? Kortom, hoe maak ik een dynamische insert als ik niet weet op voorhand welke velden ik heb?
Extra info: Postgres 9.1, Windows 7 en Redhat 6.1.
Voor een project ben ik bezig om een dynamische database te maken.
Wat houdt dit in?
De gebruiker moet on-the-fly onderwerpen (Tabellen) aan kunnen maken
Deze tabellen moeten zelf in te delen zijn.
Hiervoor heb ik de volgende aanpak gedaan:
Er is een hoofdtabel (in dit geval: ITO) waar alle metadata in staat:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
| CREATE TABLE ito ( id serial NOT NULL, name character varying(255) NOT NULL, description text NOT NULL, status smallint, version integer, changes text, ito_table_name character varying(255), ito_owner bigint, ito_creator bigint, ); |
met nog een aantal FK's e.d.
De metadata van de velden in de dynamische tabellen worden ook opgeslagen, verrassend in de tabel: ito_fields:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| CREATE TABLE ito_fields ( id serial NOT NULL, guid uuid NOT NULL DEFAULT (md5(((nextval('guid_id_seq'::regclass) || ''::text) || now())))::uuid, column_name character varying(255) NOT NULL, name character varying(100) NOT NULL, description character varying(255) NOT NULL, status smallint, indexed boolean, field_version integer, semantic_change_description text, syntactic_change_description text, ito_id bigint NOT NULL, fieldtype integer NOT NULL, ); |
Waarbij fieldtype verwijst naar een native datatype.
Als deze 2 tabellen zijn gevuld voor een onderwerp, wordt de dynamische tabel gecreëerd. dit werkt als een tierelier.
Maar dan:
De users hebben geen toegang op de tabellen zelf. Kortom, via functions wordt de data ontsloten. Hier komt mijn probleem:
Vanuit een functie moet er data toegevoegd worden aan een dynamische tabel.
Omdat je niet weet hoeveel kolommen er in een tabel zitten, is er hier gekozen voor een array. Om toch alle datatypes te ondersteunen, is er gekozen voor een array of text (text[]).
Voor integers, booleans en double precisions is dit geen probleem, maar bij het invoegen van text gaat het mis.
SQL:
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
| CREATE OR REPLACE FUNCTION add(id integer, fields text[]) RETURNS integer AS $BODY$ DECLARE l_line_ending text := ')'; l_fieldtype integer; l_ito_table_name text; l_ito_fieldnames text; l_field ito_fields%rowtype; l_first_loop boolean := true; l_values_to_insert text := 'VALUES ('; l_loop_counter integer := 0; l_query text; BEGIN select into l_ito_table_name ito_table_name from ito where id = target_ito_id; l_ito_fieldnames := 'insert into ' || l_ito_table_name || '('; FOR l_field IN SELECT * FROM ito_fields WHERE ito_fields.ito_id = target_ito_id order by ito_fields.id asc LOOP l_loop_counter := l_loop_counter +1; l_fieldtype := l_field.fieldtype; if not l_first_loop THEN l_values_to_insert := (l_values_to_insert || ', '); end if; if l_field.fieldtype = 1 THEN l_values_to_insert := (l_values_to_insert || '''' || (fields[l_loop_counter]) || '''' ); elsif l_field.fieldtype = 2 THEN l_values_to_insert := quote_literal(l_values_to_insert || private.cast_to_integer(fields[l_loop_counter])); elsif l_field.fieldtype = 3 THEN l_values_to_insert := quote_literal(l_values_to_insert || private.cast_to_boolean(fields[l_loop_counter])); elsif l_field.fieldtype = 4 THEN l_values_to_insert := quote_literal(l_values_to_insert || private.cast_to_float(fields[l_loop_counter])); else return 103; end if; if l_first_loop then l_ito_fieldnames := l_ito_fieldnames || l_field.column_name; l_first_loop := false; else l_ito_fieldnames := l_ito_fieldnames || ', ' || l_field.column_name; end if; END LOOP; l_ito_fieldnames := l_ito_fieldnames || l_line_ending; l_values_to_insert := ((l_values_to_insert) || (l_line_ending)); l_query := (l_ito_fieldnames || l_values_to_insert); EXECUTE l_query; return 0; END; |
Omdat de query aan elkaar geconcatenate wordt, gaat het mis bij het toevoegen van de (single) quotes. Postgres vertikt het om te begrijpen dat het geëscapede quotes zijn. Zelfs bij een constructie als geescapede quotes gaat het nog mis.
Ook bracht de quote_literal/quote_nullable geen uitkomst.
Zie ik hier iets over het hoofd, is er een makkelijkere oplossing? Kortom, hoe maak ik een dynamische insert als ik niet weet op voorhand welke velden ik heb?
Extra info: Postgres 9.1, Windows 7 en Redhat 6.1.