Skip to content

Instantly share code, notes, and snippets.

@angoca
Last active May 21, 2021 04:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save angoca/f41f8b7e71be8b701c3081bc06af2cab to your computer and use it in GitHub Desktop.
Save angoca/f41f8b7e71be8b701c3081bc06af2cab to your computer and use it in GitHub Desktop.
Generate Insert statement from Select
--#SET TERMINATOR @
--SET SERVEROUTPUT ON@
/**
* Function that returns a string containing the insert statement with the
* values from the fields of a given row id. The case of the tableschame or of
* the tablename could be ignored if the name is stored in uppercase.
* This function does not generate the columns for the following datatypes:
* - XML
* - BLOB
* - BINARY
* - VARBINARY
* - BOOLEAN
* When a table only contains the previous values, then the statement cannot
* be generated.
* If you want to know how the function is working, you can activate the server
* output to see the partial queries:
* SET SERVEROUTPUT ON
* This function throws the following signals:
* - DBA01: When there is argument problem.
* - DBA02: If the tablel cannot be found.
* - DBA03: Table structure cannot be parsed.
*
* IN schemaname
* Schema of the table. If null, then current schema value will be used.
* IN tablename
* Name of the table.
* RETURNS A string with the Insert statement.
*/
CREATE OR REPLACE FUNCTION generate_insert(
schemaname ANCHOR SYSCAT.TABLES.TABSCHEMA,
tablename ANCHOR SYSCAT.TABLES.TABNAME,
rownu INTEGER
)
RETURNS VARCHAR(32672)
BEGIN
DECLARE qty SMALLINT;
DECLARE stmt1 VARCHAR(32672);
DECLARE stmt2 VARCHAR(32672);
DECLARE INVALID_ARGUMENT CONDITION FOR SQLSTATE 'DBA01';
DECLARE OBJECT_NOT_FOUND CONDITION FOR SQLSTATE 'DBA02';
DECLARE cursor1 CURSOR FOR
WITH COLUMNS (SCHEMA, TABLE, COLUMN) AS (
SELECT
TABSCHEMA, TABNAME, COLNAME
FROM SYSCAT.COLUMNS
WHERE TABSCHEMA = schemaname
AND TABNAME = tablename
AND TYPENAME NOT IN ('XML', 'BLOB', 'BINARY', 'VARBINARY' , 'BOOLEAN')
ORDER BY COLNO
)
SELECT
'SELECT ''INSERT INTO "' || SCHEMA || '"."' || TABLE
|| '" (' || LISTAGG('"' || COLUMN, '", ') || '") VALUES ('''
|| LISTAGG(' || CASE WHEN "' || COLUMN
|| '" IS NOT NULL THEN '''''''' || REPLACE("' || COLUMN
|| '", '''''''', '''''''''''') || '''''''' ELSE ''NULL'' END || ''',
', ''') || ')'' FROM "' || SCHEMA || '"."' || TABLE || '" WHERE RID() = '
|| rownu
FROM COLUMNS
GROUP BY SCHEMA, TABLE
;
DECLARE cursor2 CURSOR
FOR dynsql;
DECLARE EXIT HANDLER FOR SQLSTATE '22004'
BEGIN
CALL DBMS_OUTPUT.PUT_LINE('Error during query');
RESIGNAL SQLSTATE 'DBA03'
SET MESSAGE_TEXT = 'The table structure cannot be processed';
END;
CALL DBMS_OUTPUT.PUT_LINE('Started');
-- Validates the input parameters.
IF (tablename IS NULL OR tablename = '') THEN
SIGNAL INVALID_ARGUMENT
SET MESSAGE_TEXT = 'The tablename cannot be null or empty';
END IF;
IF (rownu <= 0) THEN
SIGNAL INVALID_ARGUMENT
SET MESSAGE_TEXT = 'The rownumber cannot be zero or negative';
END IF;
IF (schemaname = '') THEN
SIGNAL INVALID_ARGUMENT
SET MESSAGE_TEXT = 'The schemaname cannot be empty';
END IF;
IF (schemaname IS NULL) THEN
SET schemaname = CURRENT SCHEMA;
END IF;
SET schemaname = TRIM(schemaname);
SET tablename = TRIM(tablename);
-- Checks if the table exists.
SET qty = (
SELECT COUNT(1)
FROM SYSCAT.TABLES
WHERE TABSCHEMA = schemaname AND TABNAME = tablename);
IF (qty = 0) THEN
SET schemaname = UPPER(schemaname);
SET tablename = UPPER(tablename);
SET qty = (
SELECT COUNT(1)
FROM SYSCAT.TABLES
WHERE TABSCHEMA = schemaname AND TABNAME = tablename);
IF (qty = 0) THEN
SIGNAL OBJECT_NOT_FOUND
SET MESSAGE_TEXT = 'The table object was not found.';
ELSEIF (qty > 1) THEN
SIGNAL OBJECT_NOT_FOUND
SET MESSAGE_TEXT = 'Several objects has this name. SP error.';
END IF;
ELSEIF (qty > 1) THEN
SIGNAL OBJECT_NOT_FOUND
SET MESSAGE_TEXT = 'Several objects has this name. SP error.';
END IF;
-- Generates the Select to get the values from the table.
OPEN cursor1;
FETCH FROM cursor1 INTO stmt1;
CALL DBMS_OUTPUT.PUT_LINE(SUBSTR(stmt1, 1, 1000));
CLOSE cursor1;
-- Generates the Insert statements with the field values.
PREPARE dynsql FROM stmt1;
OPEN cursor2;
FETCH FROM cursor2 INTO stmt2;
CALL DBMS_OUTPUT.PUT_LINE(SUBSTR(stmt2, 1, 1000));
CLOSE cursor2;
CALL DBMS_OUTPUT.PUT_LINE('Finished');
-- Result set with the insert values.
RETURN stmt2;
END
@
-- Tests for signals.
-- Empty schema. ERROR raised.
VALUES SUBSTR(generate_insert('', 'EMPLOYEE', 1), 1, 100)@
-- Table null. ERROR raised.
VALUES SUBSTR(generate_insert('DB2INST1', NULL, 1), 1, 100)@
-- Table empty. ERROR raised.
VALUES SUBSTR(generate_insert('DB2INST1', '', 1), 1, 100)@
-- Rowid -1. ERROR raised.
VALUES SUBSTR(generate_insert('DB2INST1', 'EMPLOYEE', -1), 1, 100)@
-- Rowid 0. ERROR raised.
VALUES SUBSTR(generate_insert('DB2INST1', 'EMPLOYEE', 0), 1, 100)@
-- Unexisting schema. ERROR raised.
VALUES SUBSTR(generate_insert('UNEXISTING', 'EMPLOYEE', 1), 1, 100)@
-- Unexisting table. ERROR raised.
VALUES SUBSTR(generate_insert('DB2INST1', 'UNEXISTING', 1), 1, 100)@
-- Creates tables for different kind of tests, and populates them.
CREATE TABLE TEST_ONE_TIME_COL (COL1 TIME)@
INSERT INTO TEST_ONE_TIME_COL VALUES (CURRENT TIME)@
CREATE TABLE TEST_ONE_DATE_COL (COL1 DATE)@
INSERT INTO TEST_ONE_DATE_COL VALUES (CURRENT DATE)@
CREATE TABLE TEST_ONE_TIMESTAMP_COL (COL1 TIMESTAMP)@
INSERT INTO TEST_ONE_TIMESTAMP_COL VALUES (CURRENT TIMESTAMP)@
CREATE TABLE TEST_ONE_CHAR_COL (COL1 CHAR(10))@
INSERT INTO TEST_ONE_CHAR_COL VALUES ('CHAR')@
CREATE TABLE TEST_ONE_VARCHAR_COL (COL1 VARCHAR(10))@
INSERT INTO TEST_ONE_VARCHAR_COL VALUES ('CHAR')@
CREATE TABLE TEST_ONE_CLOB_COL (COL1 CLOB)@
INSERT INTO TEST_ONE_CLOB_COL VALUES ('CLOB')@
CREATE TABLE TEST_ONE_GRAPHIC_COL (COL1 GRAPHIC(10))@
INSERT INTO TEST_ONE_GRAPHIC_COL VALUES ('GRAPHIC')@
CREATE TABLE TEST_ONE_VARGRAPHIC_COL (COL1 VARGRAPHIC(10))@
INSERT INTO TEST_ONE_VARGRAPHIC_COL VALUES ('GRAPHIC')@
CREATE TABLE TEST_ONE_DBCLOB_COL (COL1 DBCLOB)@
INSERT INTO TEST_ONE_DBCLOB_COL VALUES ('DBCLOB')@
CREATE TABLE TEST_ONE_SMALLINT_COL (COL1 SMALLINT)@
INSERT INTO TEST_ONE_SMALLINT_COL VALUES (32767)@
CREATE TABLE TEST_ONE_INT_COL (COL1 INTEGER)@
INSERT INTO TEST_ONE_INT_COL VALUES (2147483647)@
CREATE TABLE TEST_ONE_BIGINT_COL (COL1 BIGINT)@
INSERT INTO TEST_ONE_BIGINT_COL VALUES (9223372036854775807)@
CREATE TABLE TEST_ONE_DECIMAL_COL (COL1 DECIMAL(31,16))@
INSERT INTO TEST_ONE_DECIMAL_COL VALUES (1234567890.0123456789)@
CREATE TABLE TEST_ONE_DECFLOAT_COL (COL1 DECFLOAT)@
INSERT INTO TEST_ONE_DECFLOAT_COL VALUES (-9.999999999999999e307)@
INSERT INTO TEST_ONE_DECFLOAT_COL VALUES (1.000000000000000e-307)@
INSERT INTO TEST_ONE_DECFLOAT_COL VALUES (NAN)@
INSERT INTO TEST_ONE_DECFLOAT_COL VALUES (-SNAN)@
INSERT INTO TEST_ONE_DECFLOAT_COL VALUES (INFINITY)@
CREATE TABLE TEST_ONE_REAL_COL (COL1 REAL)@
INSERT INTO TEST_ONE_REAL_COL VALUES (3.402E+38)@
CREATE TABLE TEST_ONE_DOUBLE_COL (COL1 DOUBLE)@
INSERT INTO TEST_ONE_DOUBLE_COL VALUES (1.79769E+308)@
CREATE TABLE TEST_TWO_COLUMNS (COL1 INTEGER, COL2 CHAR(12))@
INSERT INTO TEST_TWO_COLUMNS VALUES (1, 'A')@
CREATE TABLE "TEST_Lower_Case" (COL1 INTEGER)@
INSERT INTO "TEST_Lower_Case" VALUES (1)@
CREATE TABLE TEST_LOWER_CASE ("ColLower" INTEGER)@
INSERT INTO TEST_LOWER_CASE VALUES (1)@
CREATE TABLE TEST_NULL (COL1 INTEGER)@
INSERT INTO TEST_NULL VALUES (NULL)@
CREATE TABLE TEST_SINGLE_QUOTE (COL1 VARCHAR(10))@
INSERT INTO TEST_SINGLE_QUOTE VALUES ('O''connor')@
CREATE TABLE TEST_DOUBLE_QUOTE (COL1 VARCHAR(10))@
INSERT INTO TEST_DOUBLE_QUOTE VALUES ('"AngocA"')@
CREATE TABLE TEST_DOUBLE_BYTE_CHAR (COL1 VARCHAR(20))@
INSERT INTO TEST_DOUBLE_BYTE_CHAR VALUES (u&'Andr\00E9s')@
CREATE TABLE TEST_MULTIPLE_ROWS (COL1 INTEGER, COL2 CHAR(10))@
INSERT INTO TEST_MULTIPLE_ROWS VALUES
(1, 'A'),
(2, 'B'),
(3, 'C')
@
-- Table with on time column. Null schema, then current schema.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_TIME_COL', 4), 1, 100)@
-- Table with on date column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_DATE_COL', 4), 1, 100)@
-- Table with on timestamp column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_TIMESTAMP_COL', 4), 1, 100)@
-- Table with on char column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_CHAR_COL', 4), 1, 100)@
-- Table with on varchar column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_VARCHAR_COL', 4), 1, 100)@
-- Table with on clob column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_CLOB_COL', 4), 1, 100)@
-- Table with on graphic column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_GRAPHIC_COL', 4), 1, 100)@
-- Table with on vargraphic column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_VARGRAPHIC_COL', 4), 1, 100)@
-- Table with on dbclob column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_DBCLOB_COL', 4), 1, 100)@
-- Table with on smallint column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_SMALLINT_COL', 4), 1, 100)@
-- Table with on int column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_INT_COL', 4), 1, 100)@
-- Table with on bigint column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_BIGINT_COL', 4), 1, 100)@
-- Table with on decimal column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_DECIMAL_COL', 4), 1, 100)@
-- Table with on decfloat column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_DECFLOAT_COL', 4), 1, 100)@
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_DECFLOAT_COL', 5), 1, 100)@
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_DECFLOAT_COL', 6), 1, 100)@
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_DECFLOAT_COL', 7), 1, 100)@
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_DECFLOAT_COL', 8), 1, 100)@
-- Table with on real column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_REAL_COL', 4), 1, 100)@
-- Table with on double column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_DOUBLE_COL', 4), 1, 100)@
-- Table with 2 columns.
VALUES SUBSTR(generate_insert(NULL, 'TEST_TWO_COLUMNS', 4), 1, 150)@
-- Lower case tablename.
VALUES SUBSTR(generate_insert(NULL, 'TEST_Lower_Case', 4), 1, 100)@
-- Lower case colname.
VALUES SUBSTR(generate_insert(NULL, 'TEST_LOWER_CASE', 4), 1, 100)@
-- Null value.
VALUES SUBSTR(generate_insert(NULL, 'TEST_NULL', 4), 1, 100)@
-- Single quote.
VALUES SUBSTR(generate_insert(NULL, 'TEST_SINGLE_QUOTE', 4), 1, 100)@
-- Double quote.
VALUES SUBSTR(generate_insert(NULL, 'TEST_DOUBLE_QUOTE', 4), 1, 100)@
-- Double byte characters.
VALUES SUBSTR(generate_insert(NULL, 'TEST_DOUBLE_BYTE_CHAR', 4), 1, 100)@
-- Multiple rows.
SELECT SUBSTR(generate_insert(NULL, 'TEST_MULTIPLE_ROWS', RID()) || ';', 1, 99)
FROM TEST_MULTIPLE_ROWS@
-- Name given in lowercase, converted to uppercase.
VALUES SUBSTR(generate_insert(NULL, 'test_one_int_col', 4), 1, 100)@
-- Name given with extra spaces.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_INT_COL ', 4), 1, 100)@
VALUES SUBSTR(generate_insert(NULL, ' TEST_ONE_INT_COL', 4), 1, 100)@
VALUES SUBSTR(generate_insert(NULL, ' TEST_ONE_INT_COL ', 4), 1, 100)@
-- Clean the database from test tables.
DROP TABLE TEST_MULTIPLE_ROWS@
DROP TABLE TEST_DOUBLE_BYTE_CHAR@
DROP TABLE TEST_DOUBLE_QUOTE@
DROP TABLE TEST_SINGLE_QUOTE@
DROP TABLE TEST_NULL@
DROP TABLE TEST_LOWER_CASE@
DROP TABLE "TEST_Lower_Case"@
DROP TABLE TEST_TWO_COLUMNS@
DROP TABLE TEST_ONE_DOUBLE_COL@
DROP TABLE TEST_ONE_REAL_COL@
DROP TABLE TEST_ONE_DECFLOAT_COL@
DROP TABLE TEST_ONE_DECIMAL_COL@
DROP TABLE TEST_ONE_BIGINT_COL@
DROP TABLE TEST_ONE_INT_COL@
DROP TABLE TEST_ONE_SMALLINT_COL@
DROP TABLE TEST_ONE_DBCLOB_COL@
DROP TABLE TEST_ONE_VARGRAPHIC_COL@
DROP TABLE TEST_ONE_GRAPHIC_COL@
DROP TABLE TEST_ONE_CLOB_COL@
DROP TABLE TEST_ONE_VARCHAR_COL@
DROP TABLE TEST_ONE_CHAR_COL@
DROP TABLE TEST_ONE_TIMESTAMP_COL@
DROP TABLE TEST_ONE_DATE_COL@
DROP TABLE TEST_ONE_TIME_COL@
--SET SERVEROUTPUT OFF@
--==========
-- These are the cases for not supported data types.
-- XML could contain different characters that makes the query very complex.
-- BLOB has to be casted as
-- BINARY and VARBINARY are internal data types.
-- BOOLEAN is valid from 11.5.
CREATE TABLE TEST_ONE_XML_COL (COL1 XML)@
INSERT INTO TEST_ONE_XML_COL VALUES ('<person/>')@
INSERT INTO TEST_ONE_XML_COL VALUES ('<person><name>Andres</name></person>')@
INSERT INTO TEST_ONE_XML_COL VALUES ('<person positition="dba"></person>')@
CREATE TABLE TEST_ONE_BLOB_COL (COL1 BLOB)@
INSERT INTO TEST_ONE_BLOB_COL VALUES (CAST('BLOB' AS BLOB))@
CREATE TABLE TEST_ONE_BINARY_COL (COL1 BINARY)@
INSERT INTO TEST_ONE_BINARY_COL VALUES ('BINARY')@
CREATE TABLE TEST_ONE_VARBINARY_COL (COL1 VARBINARY)@
INSERT INTO TEST_ONE_VARBINARY_COL VALUES ('VARBINARY')@
CREATE TABLE TEST_ONE_BOOLEAN_COL (COL1 BOOLEAN)@
INSERT INTO TEST_ONE_BOOLEAN_COL VALUES ('true')@
-- Table with on xml column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_XML_COL', 4), 1, 100)@
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_XML_COL', 5), 1, 100)@
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_XML_COL', 6), 1, 100)@
-- Table with on blob column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_BLOB_COL', 4), 1, 100)@
-- Table with on binary column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_BINARY_COL', 4), 1, 100)@
-- Table with on varbinary column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_VARBINARY_COL', 4), 1, 100)@
-- Only works with Db2 v11.5 or higher.
-- Table with on boolean column.
VALUES SUBSTR(generate_insert(NULL, 'TEST_ONE_BOOLEAN_COL', 4), 1, 100)@
DROP TABLE TEST_ONE_XML_COL@
DROP TABLE TEST_ONE_BLOB_COL@
DROP TABLE TEST_ONE_BOOLEAN_COL@
DROP TABLE TEST_ONE_VARBINARY_COL@
DROP TABLE TEST_ONE_BINARY_COL@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment