Changeset 23 for trunk/epic/epic.sql

Show
Ignore:
Timestamp:
09/07/08 23:57:57 (4 years ago)
Author:
fumanchu
Message:

Lots of changes:

  1. Changed global to return the TEMP tablename instead of a record. Use the new test.get function to obtain a record.
  2. Added a 'name' arg to global to re-use the TEMP table.
  3. New test.get(call, rownum).
  4. Moved the few public functions into the test schema.
  5. Moved the under-under attributes to new functions: constructor, len, iter.
  6. Made run_test prepend the test schema to search_path.
Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/epic/epic.sql

    r22 r23  
    227227 
    228228 
    229 CREATE OR REPLACE FUNCTION _ensure_globals() RETURNS boolean AS $$ 
     229CREATE OR REPLACE FUNCTION test._ensure_globals() RETURNS boolean AS $$ 
    230230BEGIN 
    231231  SET client_min_messages = warning; 
    232232   
    233233  BEGIN 
    234     CREATE SEQUENCE _global_ids; 
     234    CREATE SEQUENCE test._global_ids; 
    235235  EXCEPTION WHEN duplicate_table THEN 
    236236    NULL; 
     
    240240END; 
    241241$$ LANGUAGE plpgsql; 
    242 SELECT * FROM _ensure_globals(); 
    243 DROP FUNCTION _ensure_globals(); 
    244  
    245  
    246 -- Note this is in the public schema!! 
    247 CREATE OR REPLACE FUNCTION _global_record(tablename text, creator text) RETURNS record AS $$ 
    248 DECLARE 
    249   result         record; 
    250   record_stmt    text; 
    251 BEGIN 
    252   EXECUTE 'SELECT NULL::text AS __name__, NULL::text AS __create__, ' 
    253        || 'NULL::text AS __record__, NULL::text AS __iter__, ' 
    254        || 'NULL::text AS __attributes__, 0::int AS __len__, ' 
    255        || '* FROM ' || tablename || ' LIMIT 1' INTO result; 
    256    
    257   -- We add these values outside the EXECUTE in case the TEMP table has no rows. 
    258   result.__name__ := tablename; 
    259   result.__create__ := creator; 
    260   result.__record__ := 'SELECT * FROM _global_record(''' || tablename || ''', ''' || creator || ''')'; 
    261   result.__iter__ := 'SELECT * FROM ' || tablename; 
    262   result.__attributes__ := 'SELECT attname FROM test.attributes(''' || tablename || ''')'; 
    263   EXECUTE 'SELECT COUNT(*) FROM ' || tablename INTO result.__len__; 
    264    
    265   RETURN result; 
    266 END; 
    267 $$ LANGUAGE plpgsql; 
    268  
    269  
    270 -- Note this is in the public schema!! 
    271 CREATE OR REPLACE FUNCTION global(call text) RETURNS record AS $$ 
    272 -- Stores the given call's output in a TEMP table, and returns it as a record. 
    273 -- If the call produces several rows, only the first record is returned. 
     242SELECT * FROM test._ensure_globals(); 
     243DROP FUNCTION test._ensure_globals(); 
     244 
     245 
     246CREATE OR REPLACE FUNCTION test.global(call text, name text) RETURNS text AS $$ 
     247-- Stores the given call's output in a TEMP table, and returns the TEMP table name. 
    274248--  
    275249-- 'call' can be any SELECT, table, view, or procedure that returns records. 
     250DECLARE 
     251  tablename      text; 
     252  creator        text; 
     253BEGIN 
     254  IF name IS NULL THEN 
     255    tablename := '_global_' || nextval('_global_ids'); 
     256  ELSE 
     257    tablename := name; 
     258  END IF; 
     259   
     260  BEGIN 
     261    EXECUTE 'DROP TABLE ' || tablename; 
     262  EXCEPTION WHEN undefined_table THEN 
     263    NULL; 
     264  END; 
     265   
     266  creator := test.statement(call); 
     267  EXECUTE 'CREATE TEMP TABLE ' || tablename || ' WITHOUT OIDS AS ' || creator; 
     268  EXECUTE 'COMMENT ON TABLE ' || tablename || ' IS ' || quote_literal(creator); 
     269  RETURN tablename; 
     270END; 
     271$$ LANGUAGE plpgsql; 
     272 
     273CREATE OR REPLACE FUNCTION test.global(call text) RETURNS text AS $$ 
     274  SELECT global FROM test.global($1, NULL); 
     275$$ LANGUAGE SQL; 
     276 
     277CREATE OR REPLACE FUNCTION test.get(p_tablename text, p_offset int) RETURNS record AS $$ 
     278-- Returns a record (the first one, by default) from the given global table. 
    276279--  
    277280-- The returned record includes the following additional attributes: 
     
    280283--       such as assert_column, assert_values, and assert_empty (since no 
    281284--       procedural languages support passing records as args). 
    282 --   * __create__ (text): The SQL statement used to construct the table. 
    283 --   * __record__ (text): The SQL statement used to construct the returned record. 
    284 --       Use this to copy the returned record (perhaps in another function). 
    285 --       Example: EXECUTE g.__record__ INTO g2; 
    286 --   * __iter__ (text): An SQL string to SELECT * FROM the TEMP table. 
    287 --       Example: FOR record IN EXECUTE g.__iter__ 
    288 --   * __attributes__ (text): The SQL string for TEMP table column names. 
    289 --       Example: FOR colname IN EXECUTE g.__attributes__ 
    290 --   * __len__ (int): The number of rows in the TEMP table. This value 
    291 --       is NOT updated if you change the table after its creation. 
    292 DECLARE 
    293   tablename      text; 
    294   creator        text; 
     285DECLARE 
    295286  result         record; 
    296 BEGIN 
    297   tablename := '_global_' || nextval('_global_ids'); 
    298   creator := test.statement(call); 
    299   EXECUTE 'CREATE TEMP TABLE ' || tablename || ' AS ' || creator; 
    300   result := _global_record(tablename, creator); 
     287  rownum         int; 
     288BEGIN 
     289  IF p_offset IS NULL OR p_offset < 0 THEN 
     290    rownum := 0; 
     291  ELSE 
     292    rownum := p_offset; 
     293  END IF; 
     294   
     295  EXECUTE 'SELECT *, NULL::text AS __name__ FROM ' || p_tablename || ' LIMIT 1 OFFSET ' || rownum INTO result; 
     296   
     297  -- We add these values outside the EXECUTE in case the SELECT returned no rows. 
     298  result.__name__ := p_tablename; 
     299   
    301300  RETURN result; 
    302301END; 
    303302$$ LANGUAGE plpgsql; 
    304303 
     304CREATE OR REPLACE FUNCTION test.get(p_tablename text) RETURNS record AS $$ 
     305DECLARE 
     306  result         record; 
     307BEGIN 
     308  EXECUTE 'SELECT *, NULL::text AS __name__ FROM ' || p_tablename || ' LIMIT 1' INTO result; 
     309   
     310  -- We add these values outside the EXECUTE in case the SELECT returned no rows. 
     311  result.__name__ := p_tablename; 
     312   
     313  RETURN result; 
     314END; 
     315$$ LANGUAGE plpgsql; 
     316 
     317 
     318CREATE OR REPLACE FUNCTION test.constructor(tablename text) RETURNS text AS $$ 
     319-- Return the SQL statement used to construct the given global table. 
     320  SELECT obj_description(pgc.oid, 'pg_class') FROM pg_class pgc WHERE relname = $1; 
     321$$ LANGUAGE SQL; 
     322 
     323 
     324CREATE OR REPLACE FUNCTION test.len(tablename text) RETURNS int AS $$ 
     325-- Return the number of rows in the given table. 
     326DECLARE 
     327  num    int; 
     328BEGIN 
     329  EXECUTE 'SELECT COUNT(*) FROM ' || tablename INTO num; 
     330  RETURN num; 
     331END 
     332$$ LANGUAGE plpgsql; 
     333 
     334 
     335CREATE OR REPLACE FUNCTION test.iter(tablename text) RETURNS text AS $$ 
     336-- Return SQL to retrieve all rows in the given table. 
     337  SELECT 'SELECT * FROM ' || $1 
     338$$ LANGUAGE sql; 
     339 
    305340 
    306341CREATE OR REPLACE FUNCTION test.attributes(tablename text) RETURNS SETOF pg_attribute AS $$ 
    307342DECLARE 
    308343  rec      record; 
     344  seen     int := 0; 
    309345BEGIN 
    310346  FOR rec IN 
     
    314350    AND attnum >= 1 
    315351  LOOP 
     352    seen := seen + 1; 
    316353    RETURN NEXT rec; 
    317354  END LOOP; 
    318 END; 
    319 $$ LANGUAGE plpgsql; 
    320  
    321  
    322 CREATE OR REPLACE FUNCTION typename(elem anyelement) RETURNS text AS $$ 
     355   
     356  IF seen = 0 THEN 
     357    RAISE EXCEPTION '% has no attributes.', quote_literal(tablename); 
     358  END IF; 
     359END; 
     360$$ LANGUAGE plpgsql; 
     361 
     362 
     363CREATE OR REPLACE FUNCTION test.typename(elem anyelement) RETURNS text AS $$ 
    323364-- Return the typename of the given element. 
    324365DECLARE 
     
    345386  output_record   test.results%ROWTYPE; 
    346387  splitpoint      int; 
     388  old_search_path text; 
    347389BEGIN 
    348390  SELECT module INTO modulename FROM test.testnames WHERE name = testname; 
     
    350392   
    351393  BEGIN 
     394    -- Allow test.* functions to be referenced without a schema name during this transaction. 
     395    PERFORM set_config('search_path', 'test, ' || current_setting('search_path'), true); 
    352396    EXECUTE 'SELECT * FROM test.' || testname || '();'; 
    353397  EXCEPTION WHEN OTHERS THEN 
     
    629673      RETURN; 
    630674  END; 
    631   RAISE EXCEPTION 'Call: ''%'' did not raise an error.', call; 
     675  RAISE EXCEPTION 'Call: % did not raise an error.', quote_literal(call); 
    632676END; 
    633677$$ LANGUAGE plpgsql;