@cerema/cadriciel 1.6.2-beta → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/assets/docker/.env +27 -0
- package/cli/assets/docker/docker-compose.yml +209 -0
- package/cli/assets/docker/init-db/01-create-keycloak-db.sql +1 -0
- package/cli/assets/docker/init-db/02-cadriciel.sql +3604 -0
- package/cli/global/doctor.js +213 -81
- package/cli/global/init.js +34 -237
- package/cli/global/install.js +99 -20
- package/cli.js +424 -424
- package/package.json +4 -3
- /package/cli/{global → _old}/load.js +0 -0
- /package/cli/{global → _old}/login.js +0 -0
- /package/cli/{global → _old}/logout.js +0 -0
|
@@ -0,0 +1,3604 @@
|
|
|
1
|
+
--
|
|
2
|
+
-- PostgreSQL database dump
|
|
3
|
+
--
|
|
4
|
+
|
|
5
|
+
SET statement_timeout = 0;
|
|
6
|
+
SET lock_timeout = 0;
|
|
7
|
+
SET idle_in_transaction_session_timeout = 0;
|
|
8
|
+
SET client_encoding = 'UTF8';
|
|
9
|
+
SET standard_conforming_strings = on;
|
|
10
|
+
SELECT pg_catalog.set_config('search_path', '', false);
|
|
11
|
+
SET check_function_bodies = false;
|
|
12
|
+
SET xmloption = content;
|
|
13
|
+
SET client_min_messages = warning;
|
|
14
|
+
SET row_security = off;
|
|
15
|
+
--
|
|
16
|
+
-- Name: public; Type: SCHEMA; Schema: -; Owner: postgres
|
|
17
|
+
--
|
|
18
|
+
|
|
19
|
+
CREATE SCHEMA IF NOT EXISTS public;
|
|
20
|
+
ALTER SCHEMA public OWNER TO postgres;
|
|
21
|
+
CREATE SCHEMA IF NOT EXISTS auth;
|
|
22
|
+
ALTER SCHEMA auth OWNER TO postgres;
|
|
23
|
+
--
|
|
24
|
+
-- TOC entry 3177 (class 2606 OID 17326)
|
|
25
|
+
-- Name: profil profil_utilisateurid_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: postgres
|
|
26
|
+
--
|
|
27
|
+
|
|
28
|
+
--
|
|
29
|
+
-- Name: SCHEMA public; Type: COMMENT; Schema: -; Owner: postgres
|
|
30
|
+
--
|
|
31
|
+
|
|
32
|
+
COMMENT ON SCHEMA public IS 'standard public schema';
|
|
33
|
+
--
|
|
34
|
+
-- Name: cloneparms; Type: TYPE; Schema: public; Owner: postgres
|
|
35
|
+
--
|
|
36
|
+
|
|
37
|
+
CREATE TYPE public.cloneparms AS ENUM (
|
|
38
|
+
'DATA',
|
|
39
|
+
'NODATA',
|
|
40
|
+
'DDLONLY',
|
|
41
|
+
'NOOWNER',
|
|
42
|
+
'NOACL',
|
|
43
|
+
'VERBOSE',
|
|
44
|
+
'DEBUG',
|
|
45
|
+
'FILECOPY'
|
|
46
|
+
);
|
|
47
|
+
ALTER TYPE public.cloneparms OWNER TO postgres;
|
|
48
|
+
--
|
|
49
|
+
-- Name: clone_schema(text, text, public.cloneparms[]); Type: FUNCTION; Schema: public; Owner: postgres
|
|
50
|
+
--
|
|
51
|
+
|
|
52
|
+
CREATE FUNCTION public.clone_schema(
|
|
53
|
+
source_schema text,
|
|
54
|
+
dest_schema text,
|
|
55
|
+
VARIADIC arr public.cloneparms [] DEFAULT '{}'::public.cloneparms []
|
|
56
|
+
) RETURNS void LANGUAGE plpgsql AS $_$ -- This function will clone all sequences, tables, data, views & functions from any existing schema to a new one
|
|
57
|
+
-- SAMPLE CALL:
|
|
58
|
+
-- SELECT clone_schema('sample', 'sample_clone2');
|
|
59
|
+
DECLARE src_oid oid;
|
|
60
|
+
tbl_oid oid;
|
|
61
|
+
func_oid oid;
|
|
62
|
+
object text;
|
|
63
|
+
buffer text;
|
|
64
|
+
buffer2 text;
|
|
65
|
+
buffer3 text;
|
|
66
|
+
srctbl text;
|
|
67
|
+
aname text;
|
|
68
|
+
default_ text;
|
|
69
|
+
column_ text;
|
|
70
|
+
qry text;
|
|
71
|
+
ix_old_name text;
|
|
72
|
+
ix_new_name text;
|
|
73
|
+
relpersist text;
|
|
74
|
+
udt_name text;
|
|
75
|
+
udt_schema text;
|
|
76
|
+
bRelispart bool;
|
|
77
|
+
bChild bool;
|
|
78
|
+
relknd text;
|
|
79
|
+
data_type text;
|
|
80
|
+
ocomment text;
|
|
81
|
+
adef text;
|
|
82
|
+
dest_qry text;
|
|
83
|
+
v_def text;
|
|
84
|
+
part_range text;
|
|
85
|
+
src_path_old text;
|
|
86
|
+
src_path_new text;
|
|
87
|
+
aclstr text;
|
|
88
|
+
-- issue#80 initialize arrays properly
|
|
89
|
+
tblarray text [] := '{}';
|
|
90
|
+
tblarray2 text [] := '{}';
|
|
91
|
+
tblarray3 text [] := '{}';
|
|
92
|
+
tblelement text;
|
|
93
|
+
grantor text;
|
|
94
|
+
grantee text;
|
|
95
|
+
privs text;
|
|
96
|
+
seqval bigint;
|
|
97
|
+
sq_last_value bigint;
|
|
98
|
+
sq_max_value bigint;
|
|
99
|
+
sq_start_value bigint;
|
|
100
|
+
sq_increment_by bigint;
|
|
101
|
+
sq_min_value bigint;
|
|
102
|
+
sq_cache_value bigint;
|
|
103
|
+
sq_is_called boolean := True;
|
|
104
|
+
sq_is_cycled boolean;
|
|
105
|
+
is_prokind boolean;
|
|
106
|
+
abool boolean;
|
|
107
|
+
sq_data_type text;
|
|
108
|
+
sq_cycled char(10);
|
|
109
|
+
sq_owned text;
|
|
110
|
+
sq_version text;
|
|
111
|
+
sq_server_version text;
|
|
112
|
+
sq_server_version_num integer;
|
|
113
|
+
bWindows boolean;
|
|
114
|
+
arec RECORD;
|
|
115
|
+
cnt integer;
|
|
116
|
+
cnt1 integer;
|
|
117
|
+
cnt2 integer;
|
|
118
|
+
cnt3 integer;
|
|
119
|
+
cnt4 integer;
|
|
120
|
+
pos integer;
|
|
121
|
+
tblscopied integer := 0;
|
|
122
|
+
l_child integer;
|
|
123
|
+
action text := 'N/A';
|
|
124
|
+
tblname text;
|
|
125
|
+
v_ret text;
|
|
126
|
+
v_diag1 text;
|
|
127
|
+
v_diag2 text;
|
|
128
|
+
v_diag3 text;
|
|
129
|
+
v_diag4 text;
|
|
130
|
+
v_diag5 text;
|
|
131
|
+
v_diag6 text;
|
|
132
|
+
v_dummy text;
|
|
133
|
+
spath text;
|
|
134
|
+
spath_tmp text;
|
|
135
|
+
-- issue#86 fix
|
|
136
|
+
isGenerated text;
|
|
137
|
+
-- issue#91 fix
|
|
138
|
+
tblowner text;
|
|
139
|
+
func_owner text;
|
|
140
|
+
func_name text;
|
|
141
|
+
func_args text;
|
|
142
|
+
func_argno integer;
|
|
143
|
+
view_owner text;
|
|
144
|
+
-- issue#92
|
|
145
|
+
calleruser text;
|
|
146
|
+
-- issue#94
|
|
147
|
+
bData boolean := False;
|
|
148
|
+
bDDLOnly boolean := False;
|
|
149
|
+
bVerbose boolean := False;
|
|
150
|
+
bDebug boolean := False;
|
|
151
|
+
bNoACL boolean := False;
|
|
152
|
+
bNoOwner boolean := False;
|
|
153
|
+
arglen integer;
|
|
154
|
+
vargs text;
|
|
155
|
+
avarg public.cloneparms;
|
|
156
|
+
-- issue#98
|
|
157
|
+
mvarray text [] := '{}';
|
|
158
|
+
mvscopied integer := 0;
|
|
159
|
+
-- issue#99 tablespaces
|
|
160
|
+
tblspace text;
|
|
161
|
+
-- issue#101
|
|
162
|
+
bFileCopy boolean := False;
|
|
163
|
+
t timestamptz := clock_timestamp();
|
|
164
|
+
r timestamptz;
|
|
165
|
+
s timestamptz;
|
|
166
|
+
lastsql text := '';
|
|
167
|
+
v_version text := '1.19 September 07, 2023';
|
|
168
|
+
BEGIN -- Make sure NOTICE are shown
|
|
169
|
+
SET client_min_messages = 'notice';
|
|
170
|
+
RAISE NOTICE 'clone_schema version %',
|
|
171
|
+
v_version;
|
|
172
|
+
IF 'DEBUG' = ANY ($3) THEN bDebug = True;
|
|
173
|
+
END IF;
|
|
174
|
+
IF 'VERBOSE' = ANY ($3) THEN bVerbose = True;
|
|
175
|
+
END IF;
|
|
176
|
+
-- IF bVerbose THEN RAISE NOTICE 'START: %',clock_timestamp() - t; END IF;
|
|
177
|
+
arglen := array_length($3, 1);
|
|
178
|
+
IF arglen IS NULL THEN -- nothing to do, so defaults are assumed
|
|
179
|
+
NULL;
|
|
180
|
+
ELSE -- loop thru args
|
|
181
|
+
-- IF 'NO_TRIGGERS' = ANY ($3)
|
|
182
|
+
-- select array_to_string($3, ',', '***') INTO vargs;
|
|
183
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: arguments=%',
|
|
184
|
+
$3;
|
|
185
|
+
END IF;
|
|
186
|
+
FOREACH avarg IN ARRAY $3 LOOP IF bDebug THEN RAISE NOTICE 'DEBUG: arg=%',
|
|
187
|
+
avarg;
|
|
188
|
+
END IF;
|
|
189
|
+
IF avarg = 'DATA' THEN bData = True;
|
|
190
|
+
ELSEIF avarg = 'NODATA' THEN -- already set to that by default
|
|
191
|
+
bData = False;
|
|
192
|
+
ELSEIF avarg = 'DDLONLY' THEN bDDLOnly = True;
|
|
193
|
+
ELSEIF avarg = 'NOACL' THEN bNoACL = True;
|
|
194
|
+
ELSEIF avarg = 'NOOWNER' THEN bNoOwner = True;
|
|
195
|
+
-- issue#101 fix
|
|
196
|
+
ELSEIF avarg = 'FILECOPY' THEN bFileCopy = True;
|
|
197
|
+
END IF;
|
|
198
|
+
END LOOP;
|
|
199
|
+
IF bData
|
|
200
|
+
and bDDLOnly THEN RAISE WARNING 'You can only specify DDLONLY or DATA, but not both.';
|
|
201
|
+
RETURN;
|
|
202
|
+
END IF;
|
|
203
|
+
END IF;
|
|
204
|
+
-- Get server version info to handle certain things differently based on the version.
|
|
205
|
+
SELECT setting INTO sq_server_version
|
|
206
|
+
FROM pg_settings
|
|
207
|
+
WHERE name = 'server_version';
|
|
208
|
+
SELECT version() INTO sq_version;
|
|
209
|
+
IF POSITION('compiled by Visual C++' IN sq_version) > 0 THEN bWindows = True;
|
|
210
|
+
RAISE NOTICE 'Windows: %',
|
|
211
|
+
sq_version;
|
|
212
|
+
ELSE bWindows = False;
|
|
213
|
+
RAISE NOTICE 'Linux: %',
|
|
214
|
+
sq_version;
|
|
215
|
+
END IF;
|
|
216
|
+
SELECT setting INTO sq_server_version_num
|
|
217
|
+
FROM pg_settings
|
|
218
|
+
WHERE name = 'server_version_num';
|
|
219
|
+
IF sq_server_version_num < 100000 THEN IF sq_server_version_num > 90600 THEN RAISE WARNING 'Server Version:% Number:% PG Versions older than v10 are not supported. Will try however for PG 9.6...',
|
|
220
|
+
sq_server_version,
|
|
221
|
+
sq_server_version_num;
|
|
222
|
+
ELSE RAISE WARNING 'Server Version:% Number:% PG Versions older than v10 are not supported. You need to be at minimum version 9.6 to at least try',
|
|
223
|
+
sq_server_version,
|
|
224
|
+
sq_server_version_num;
|
|
225
|
+
RETURN;
|
|
226
|
+
END IF;
|
|
227
|
+
END IF;
|
|
228
|
+
-- Check that source_schema exists
|
|
229
|
+
SELECT oid INTO src_oid
|
|
230
|
+
FROM pg_namespace
|
|
231
|
+
WHERE nspname = quote_ident(source_schema);
|
|
232
|
+
IF NOT FOUND THEN RAISE NOTICE ' source schema % does not exist!',
|
|
233
|
+
source_schema;
|
|
234
|
+
RETURN;
|
|
235
|
+
END IF;
|
|
236
|
+
-- Check for case-sensitive target schemas and reject them for now.
|
|
237
|
+
SELECT lower(dest_schema) = dest_schema INTO abool;
|
|
238
|
+
IF not abool THEN RAISE NOTICE 'Case-sensitive target schemas are not supported at this time.';
|
|
239
|
+
RETURN;
|
|
240
|
+
END IF;
|
|
241
|
+
-- Check that dest_schema does not yet exist
|
|
242
|
+
PERFORM nspname
|
|
243
|
+
FROM pg_namespace
|
|
244
|
+
WHERE nspname = quote_ident(dest_schema);
|
|
245
|
+
IF FOUND THEN RAISE NOTICE ' dest schema % already exists!',
|
|
246
|
+
dest_schema;
|
|
247
|
+
RETURN;
|
|
248
|
+
END IF;
|
|
249
|
+
IF bDDLOnly
|
|
250
|
+
and bData THEN RAISE WARNING 'You cannot specify to clone data and generate ddl at the same time.';
|
|
251
|
+
RETURN;
|
|
252
|
+
END IF;
|
|
253
|
+
-- Issue#92
|
|
254
|
+
SELECT current_user into calleruser;
|
|
255
|
+
-- Set the search_path to source schema. Before exiting set it back to what it was before.
|
|
256
|
+
-- In order to avoid issues with the special schema name "$user" that may be
|
|
257
|
+
-- returned unquoted by some applications, we ensure it remains double quoted.
|
|
258
|
+
-- MJV FIX: #47
|
|
259
|
+
SELECT setting INTO v_dummy
|
|
260
|
+
FROM pg_settings
|
|
261
|
+
WHERE name = 'search_path';
|
|
262
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: search_path=%',
|
|
263
|
+
v_dummy;
|
|
264
|
+
END IF;
|
|
265
|
+
SELECT REPLACE(
|
|
266
|
+
REPLACE(setting, '"$user"', '$user'),
|
|
267
|
+
'$user',
|
|
268
|
+
'"$user"'
|
|
269
|
+
) INTO src_path_old
|
|
270
|
+
FROM pg_settings
|
|
271
|
+
WHERE name = 'search_path';
|
|
272
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: src_path_old=%',
|
|
273
|
+
src_path_old;
|
|
274
|
+
END IF;
|
|
275
|
+
EXECUTE 'SET search_path = ' || quote_ident(source_schema);
|
|
276
|
+
SELECT setting INTO src_path_new
|
|
277
|
+
FROM pg_settings
|
|
278
|
+
WHERE name = 'search_path';
|
|
279
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: new search_path=%',
|
|
280
|
+
src_path_new;
|
|
281
|
+
END IF;
|
|
282
|
+
-- Validate required types exist. If not, create them.
|
|
283
|
+
SELECT a.objtypecnt,
|
|
284
|
+
b.permtypecnt INTO cnt,
|
|
285
|
+
cnt2
|
|
286
|
+
FROM (
|
|
287
|
+
SELECT count(*) AS objtypecnt
|
|
288
|
+
FROM pg_catalog.pg_type t
|
|
289
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
290
|
+
WHERE (
|
|
291
|
+
t.typrelid = 0
|
|
292
|
+
OR (
|
|
293
|
+
SELECT c.relkind = 'c'
|
|
294
|
+
FROM pg_catalog.pg_class c
|
|
295
|
+
WHERE c.oid = t.typrelid
|
|
296
|
+
)
|
|
297
|
+
)
|
|
298
|
+
AND NOT EXISTS (
|
|
299
|
+
SELECT 1
|
|
300
|
+
FROM pg_catalog.pg_type el
|
|
301
|
+
WHERE el.oid = t.typelem
|
|
302
|
+
AND el.typarray = t.oid
|
|
303
|
+
)
|
|
304
|
+
AND n.nspname <> 'pg_catalog'
|
|
305
|
+
AND n.nspname <> 'information_schema'
|
|
306
|
+
AND pg_catalog.pg_type_is_visible(t.oid)
|
|
307
|
+
AND pg_catalog.format_type(t.oid, NULL) = 'obj_type'
|
|
308
|
+
) a,
|
|
309
|
+
(
|
|
310
|
+
SELECT count(*) AS permtypecnt
|
|
311
|
+
FROM pg_catalog.pg_type t
|
|
312
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
313
|
+
WHERE (
|
|
314
|
+
t.typrelid = 0
|
|
315
|
+
OR (
|
|
316
|
+
SELECT c.relkind = 'c'
|
|
317
|
+
FROM pg_catalog.pg_class c
|
|
318
|
+
WHERE c.oid = t.typrelid
|
|
319
|
+
)
|
|
320
|
+
)
|
|
321
|
+
AND NOT EXISTS (
|
|
322
|
+
SELECT 1
|
|
323
|
+
FROM pg_catalog.pg_type el
|
|
324
|
+
WHERE el.oid = t.typelem
|
|
325
|
+
AND el.typarray = t.oid
|
|
326
|
+
)
|
|
327
|
+
AND n.nspname <> 'pg_catalog'
|
|
328
|
+
AND n.nspname <> 'information_schema'
|
|
329
|
+
AND pg_catalog.pg_type_is_visible(t.oid)
|
|
330
|
+
AND pg_catalog.format_type(t.oid, NULL) = 'perm_type'
|
|
331
|
+
) b;
|
|
332
|
+
IF cnt = 0 THEN CREATE TYPE obj_type AS ENUM (
|
|
333
|
+
'TABLE',
|
|
334
|
+
'VIEW',
|
|
335
|
+
'COLUMN',
|
|
336
|
+
'SEQUENCE',
|
|
337
|
+
'FUNCTION',
|
|
338
|
+
'SCHEMA',
|
|
339
|
+
'DATABASE'
|
|
340
|
+
);
|
|
341
|
+
END IF;
|
|
342
|
+
IF cnt2 = 0 THEN CREATE TYPE perm_type AS ENUM (
|
|
343
|
+
'SELECT',
|
|
344
|
+
'INSERT',
|
|
345
|
+
'UPDATE',
|
|
346
|
+
'DELETE',
|
|
347
|
+
'TRUNCATE',
|
|
348
|
+
'REFERENCES',
|
|
349
|
+
'TRIGGER',
|
|
350
|
+
'USAGE',
|
|
351
|
+
'CREATE',
|
|
352
|
+
'EXECUTE',
|
|
353
|
+
'CONNECT',
|
|
354
|
+
'TEMPORARY'
|
|
355
|
+
);
|
|
356
|
+
END IF;
|
|
357
|
+
-- Issue#95
|
|
358
|
+
SELECT pg_catalog.pg_get_userbyid(nspowner) INTO buffer
|
|
359
|
+
FROM pg_namespace
|
|
360
|
+
WHERE nspname = quote_ident(source_schema);
|
|
361
|
+
IF bDDLOnly THEN RAISE NOTICE ' Only generating DDL, not actually creating anything...';
|
|
362
|
+
-- issue#95
|
|
363
|
+
IF bNoOwner THEN RAISE INFO 'CREATE SCHEMA %;',
|
|
364
|
+
quote_ident(dest_schema);
|
|
365
|
+
ELSE RAISE INFO 'CREATE SCHEMA % AUTHORIZATION %;',
|
|
366
|
+
quote_ident(dest_schema),
|
|
367
|
+
buffer;
|
|
368
|
+
END IF;
|
|
369
|
+
RAISE NOTICE 'SET search_path=%;',
|
|
370
|
+
quote_ident(dest_schema);
|
|
371
|
+
ELSE -- issue#95
|
|
372
|
+
IF bNoOwner THEN EXECUTE 'CREATE SCHEMA ' || quote_ident(dest_schema);
|
|
373
|
+
ELSE EXECUTE 'CREATE SCHEMA ' || quote_ident(dest_schema) || ' AUTHORIZATION ' || buffer;
|
|
374
|
+
END IF;
|
|
375
|
+
END IF;
|
|
376
|
+
-- Do system table validations for subsequent system table queries
|
|
377
|
+
-- Issue#65 Fix
|
|
378
|
+
SELECT count(*) into cnt
|
|
379
|
+
FROM pg_attribute
|
|
380
|
+
WHERE attrelid = 'pg_proc'::regclass
|
|
381
|
+
AND attname = 'prokind';
|
|
382
|
+
IF cnt = 0 THEN is_prokind = False;
|
|
383
|
+
ELSE is_prokind = True;
|
|
384
|
+
END IF;
|
|
385
|
+
-- MV: Create Collations
|
|
386
|
+
action := 'Collations';
|
|
387
|
+
cnt := 0;
|
|
388
|
+
-- Issue#96 Handle differently based on PG Versions (PG15 rely on colliculocale, not collcolocate)
|
|
389
|
+
-- perhaps use this logic instead: COALESCE(c.collcollate, c.colliculocale) AS lc_collate, COALESCE(c.collctype, c.colliculocale) AS lc_type
|
|
390
|
+
IF sq_server_version_num > 150000 THEN FOR arec IN
|
|
391
|
+
SELECT n.nspname AS schemaname,
|
|
392
|
+
a.rolname AS ownername,
|
|
393
|
+
c.collname,
|
|
394
|
+
c.collprovider,
|
|
395
|
+
c.collcollate AS locale,
|
|
396
|
+
'CREATE COLLATION ' || quote_ident(dest_schema) || '."' || c.collname || '" (provider = ' || CASE
|
|
397
|
+
WHEN c.collprovider = 'i' THEN 'icu'
|
|
398
|
+
WHEN c.collprovider = 'c' THEN 'libc'
|
|
399
|
+
ELSE ''
|
|
400
|
+
END || ', locale = ''' || c.colliculocale || ''');' AS COLL_DDL
|
|
401
|
+
FROM pg_collation c
|
|
402
|
+
JOIN pg_namespace n ON (c.collnamespace = n.oid)
|
|
403
|
+
JOIN pg_roles a ON (c.collowner = a.oid)
|
|
404
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
405
|
+
ORDER BY c.collname LOOP BEGIN cnt := cnt + 1;
|
|
406
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
407
|
+
arec.coll_ddl;
|
|
408
|
+
ELSE EXECUTE arec.coll_ddl;
|
|
409
|
+
END IF;
|
|
410
|
+
END;
|
|
411
|
+
END LOOP;
|
|
412
|
+
ELSIF sq_server_version_num > 100000 THEN FOR arec IN
|
|
413
|
+
SELECT n.nspname AS schemaname,
|
|
414
|
+
a.rolname AS ownername,
|
|
415
|
+
c.collname,
|
|
416
|
+
c.collprovider,
|
|
417
|
+
c.collcollate AS locale,
|
|
418
|
+
'CREATE COLLATION ' || quote_ident(dest_schema) || '."' || c.collname || '" (provider = ' || CASE
|
|
419
|
+
WHEN c.collprovider = 'i' THEN 'icu'
|
|
420
|
+
WHEN c.collprovider = 'c' THEN 'libc'
|
|
421
|
+
ELSE ''
|
|
422
|
+
END || ', locale = ''' || c.collcollate || ''');' AS COLL_DDL
|
|
423
|
+
FROM pg_collation c
|
|
424
|
+
JOIN pg_namespace n ON (c.collnamespace = n.oid)
|
|
425
|
+
JOIN pg_roles a ON (c.collowner = a.oid)
|
|
426
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
427
|
+
ORDER BY c.collname LOOP BEGIN cnt := cnt + 1;
|
|
428
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
429
|
+
arec.coll_ddl;
|
|
430
|
+
ELSE EXECUTE arec.coll_ddl;
|
|
431
|
+
END IF;
|
|
432
|
+
END;
|
|
433
|
+
END LOOP;
|
|
434
|
+
ELSE -- handle 9.6 that is missing some columns in pg_collation
|
|
435
|
+
FOR arec IN
|
|
436
|
+
SELECT n.nspname AS schemaname,
|
|
437
|
+
a.rolname AS ownername,
|
|
438
|
+
c.collname,
|
|
439
|
+
c.collcollate AS locale,
|
|
440
|
+
'CREATE COLLATION ' || quote_ident(dest_schema) || '."' || c.collname || '" (provider = ' || ', locale = ''' || c.collcollate || ''');' AS COLL_DDL
|
|
441
|
+
FROM pg_collation c
|
|
442
|
+
JOIN pg_namespace n ON (c.collnamespace = n.oid)
|
|
443
|
+
JOIN pg_roles a ON (c.collowner = a.oid)
|
|
444
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
445
|
+
ORDER BY c.collname LOOP BEGIN cnt := cnt + 1;
|
|
446
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
447
|
+
arec.coll_ddl;
|
|
448
|
+
ELSE EXECUTE arec.coll_ddl;
|
|
449
|
+
END IF;
|
|
450
|
+
END;
|
|
451
|
+
END LOOP;
|
|
452
|
+
END IF;
|
|
453
|
+
RAISE NOTICE ' COLLATIONS cloned: %',
|
|
454
|
+
LPAD(cnt::text, 5, ' ');
|
|
455
|
+
-- MV: Create Domains
|
|
456
|
+
action := 'Domains';
|
|
457
|
+
cnt := 0;
|
|
458
|
+
FOR arec IN
|
|
459
|
+
SELECT n.nspname AS "Schema",
|
|
460
|
+
t.typname AS "Name",
|
|
461
|
+
pg_catalog.format_type(t.typbasetype, t.typtypmod) AS "Type",
|
|
462
|
+
(
|
|
463
|
+
SELECT c.collname
|
|
464
|
+
FROM pg_catalog.pg_collation c,
|
|
465
|
+
pg_catalog.pg_type bt
|
|
466
|
+
WHERE c.oid = t.typcollation
|
|
467
|
+
AND bt.oid = t.typbasetype
|
|
468
|
+
AND t.typcollation <> bt.typcollation
|
|
469
|
+
) AS "Collation",
|
|
470
|
+
CASE
|
|
471
|
+
WHEN t.typnotnull THEN 'not null'
|
|
472
|
+
END AS "Nullable",
|
|
473
|
+
t.typdefault AS "Default",
|
|
474
|
+
pg_catalog.array_to_string(
|
|
475
|
+
ARRAY (
|
|
476
|
+
SELECT pg_catalog.pg_get_constraintdef(r.oid, TRUE)
|
|
477
|
+
FROM pg_catalog.pg_constraint r -- Issue#78 FIX: handle case-sensitive names with quote_ident() on t.typename
|
|
478
|
+
WHERE t.oid = r.contypid
|
|
479
|
+
),
|
|
480
|
+
' '
|
|
481
|
+
) AS "Check",
|
|
482
|
+
'CREATE DOMAIN ' || quote_ident(dest_schema) || '.' || quote_ident(t.typname) || ' AS ' || pg_catalog.format_type(t.typbasetype, t.typtypmod) || CASE
|
|
483
|
+
WHEN t.typnotnull IS NOT NULL THEN ' NOT NULL '
|
|
484
|
+
ELSE ' '
|
|
485
|
+
END || CASE
|
|
486
|
+
WHEN t.typdefault IS NOT NULL THEN 'DEFAULT ' || t.typdefault || ' '
|
|
487
|
+
ELSE ' '
|
|
488
|
+
END || pg_catalog.array_to_string(
|
|
489
|
+
ARRAY (
|
|
490
|
+
SELECT pg_catalog.pg_get_constraintdef(r.oid, TRUE)
|
|
491
|
+
FROM pg_catalog.pg_constraint r
|
|
492
|
+
WHERE t.oid = r.contypid
|
|
493
|
+
),
|
|
494
|
+
' '
|
|
495
|
+
) || ';' AS DOM_DDL
|
|
496
|
+
FROM pg_catalog.pg_type t
|
|
497
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
498
|
+
WHERE t.typtype = 'd'
|
|
499
|
+
AND n.nspname = quote_ident(source_schema)
|
|
500
|
+
AND pg_catalog.pg_type_is_visible(t.oid)
|
|
501
|
+
ORDER BY 1,
|
|
502
|
+
2 LOOP BEGIN cnt := cnt + 1;
|
|
503
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
504
|
+
arec.dom_ddl;
|
|
505
|
+
ELSE EXECUTE arec.dom_ddl;
|
|
506
|
+
END IF;
|
|
507
|
+
END;
|
|
508
|
+
END LOOP;
|
|
509
|
+
RAISE NOTICE ' DOMAINS cloned: %',
|
|
510
|
+
LPAD(cnt::text, 5, ' ');
|
|
511
|
+
-- MV: Create types
|
|
512
|
+
action := 'Types';
|
|
513
|
+
cnt := 0;
|
|
514
|
+
lastsql = '';
|
|
515
|
+
FOR arec IN -- Fixed Issue#108:enclose double-quote roles with special characters for setting "OWNER TO"
|
|
516
|
+
-- SELECT c.relkind, n.nspname AS schemaname, t.typname AS typname, t.typcategory, pg_catalog.pg_get_userbyid(t.typowner) AS owner, CASE WHEN t.typcategory = 'C' THEN
|
|
517
|
+
SELECT c.relkind,
|
|
518
|
+
n.nspname AS schemaname,
|
|
519
|
+
t.typname AS typname,
|
|
520
|
+
t.typcategory,
|
|
521
|
+
'"' || pg_catalog.pg_get_userbyid(t.typowner) || '"' AS owner,
|
|
522
|
+
CASE
|
|
523
|
+
WHEN t.typcategory = 'C' THEN 'CREATE TYPE ' || quote_ident(dest_schema) || '.' || t.typname || ' AS (' || array_to_string(
|
|
524
|
+
array_agg(
|
|
525
|
+
a.attname || ' ' || pg_catalog.format_type(a.atttypid, a.atttypmod)
|
|
526
|
+
ORDER BY c.relname,
|
|
527
|
+
a.attnum
|
|
528
|
+
),
|
|
529
|
+
', '
|
|
530
|
+
) || ');'
|
|
531
|
+
WHEN t.typcategory = 'E' THEN 'CREATE TYPE ' || quote_ident(dest_schema) || '.' || t.typname || ' AS ENUM (' || REPLACE(
|
|
532
|
+
quote_literal(
|
|
533
|
+
array_to_string(
|
|
534
|
+
array_agg(
|
|
535
|
+
e.enumlabel
|
|
536
|
+
ORDER BY e.enumsortorder
|
|
537
|
+
),
|
|
538
|
+
','
|
|
539
|
+
)
|
|
540
|
+
),
|
|
541
|
+
',',
|
|
542
|
+
''','''
|
|
543
|
+
) || ');'
|
|
544
|
+
ELSE ''
|
|
545
|
+
END AS type_ddl
|
|
546
|
+
FROM pg_type t
|
|
547
|
+
JOIN pg_namespace n ON (n.oid = t.typnamespace)
|
|
548
|
+
LEFT JOIN pg_enum e ON (t.oid = e.enumtypid)
|
|
549
|
+
LEFT JOIN pg_class c ON (c.reltype = t.oid)
|
|
550
|
+
LEFT JOIN pg_attribute a ON (a.attrelid = c.oid)
|
|
551
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
552
|
+
AND (
|
|
553
|
+
c.relkind IS NULL
|
|
554
|
+
OR c.relkind = 'c'
|
|
555
|
+
)
|
|
556
|
+
AND t.typcategory IN ('C', 'E')
|
|
557
|
+
GROUP BY 1,
|
|
558
|
+
2,
|
|
559
|
+
3,
|
|
560
|
+
4,
|
|
561
|
+
5
|
|
562
|
+
ORDER BY n.nspname,
|
|
563
|
+
t.typcategory,
|
|
564
|
+
t.typname LOOP BEGIN cnt := cnt + 1;
|
|
565
|
+
-- Keep composite and enum types in separate branches for fine tuning later if needed.
|
|
566
|
+
IF arec.typcategory = 'E' THEN IF bDDLOnly THEN RAISE INFO '%',
|
|
567
|
+
arec.type_ddl;
|
|
568
|
+
--issue#95
|
|
569
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
570
|
+
RAISE INFO 'ALTER TYPE % OWNER TO %;',
|
|
571
|
+
quote_ident(dest_schema) || '.' || arec.typname,
|
|
572
|
+
arec.owner;
|
|
573
|
+
END IF;
|
|
574
|
+
ELSE EXECUTE arec.type_ddl;
|
|
575
|
+
--issue#95
|
|
576
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
577
|
+
EXECUTE 'ALTER TYPE ' || quote_ident(dest_schema) || '.' || arec.typname || ' OWNER TO ' || arec.owner;
|
|
578
|
+
END IF;
|
|
579
|
+
END IF;
|
|
580
|
+
ELSIF arec.typcategory = 'C' THEN IF bDDLOnly THEN RAISE INFO '%',
|
|
581
|
+
arec.type_ddl;
|
|
582
|
+
--issue#95
|
|
583
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
584
|
+
RAISE INFO 'ALTER TYPE % OWNER TO %;',
|
|
585
|
+
quote_ident(dest_schema) || '.' || arec.typname,
|
|
586
|
+
arec.owner;
|
|
587
|
+
END IF;
|
|
588
|
+
ELSE EXECUTE arec.type_ddl;
|
|
589
|
+
--issue#95
|
|
590
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
591
|
+
EXECUTE 'ALTER TYPE ' || quote_ident(dest_schema) || '.' || arec.typname || ' OWNER TO ' || arec.owner;
|
|
592
|
+
END IF;
|
|
593
|
+
END IF;
|
|
594
|
+
ELSE RAISE NOTICE ' Unhandled type:%-%',
|
|
595
|
+
arec.typcategory,
|
|
596
|
+
arec.typname;
|
|
597
|
+
END IF;
|
|
598
|
+
END;
|
|
599
|
+
END LOOP;
|
|
600
|
+
RAISE NOTICE ' TYPES cloned: %',
|
|
601
|
+
LPAD(cnt::text, 5, ' ');
|
|
602
|
+
-- Create sequences
|
|
603
|
+
action := 'Sequences';
|
|
604
|
+
cnt := 0;
|
|
605
|
+
-- fix#63 get from pg_sequences not information_schema
|
|
606
|
+
-- fix#63 take 2: get it from information_schema.sequences since we need to treat IDENTITY columns differently.
|
|
607
|
+
-- fix#95 get owner as well by joining to pg_sequences
|
|
608
|
+
-- fix#106 we can get owner info with pg_class, pg_user/pg_group, and information_schema.sequences, so we can avoid the hit to pg_sequences which is not available in 9.6
|
|
609
|
+
FOR object,
|
|
610
|
+
buffer IN -- Fixed Issue#108:
|
|
611
|
+
-- SELECT s1.sequence_name::text, s2.sequenceowner FROM information_schema.sequences s1 JOIN pg_sequences s2 ON (s1.sequence_schema = s2.schemaname AND s1.sequence_name = s2.sequencename) AND s1.sequence_schema = quote_ident(source_schema)
|
|
612
|
+
SELECT s.sequence_name::text,
|
|
613
|
+
'"' || u.usename || '"' as owner
|
|
614
|
+
FROM information_schema.sequences s
|
|
615
|
+
JOIN pg_class c ON (
|
|
616
|
+
s.sequence_name = c.relname
|
|
617
|
+
AND s.sequence_schema = c.relnamespace::regnamespace::text
|
|
618
|
+
)
|
|
619
|
+
JOIN pg_user u ON (c.relowner = u.usesysid)
|
|
620
|
+
WHERE c.relkind = 'S'
|
|
621
|
+
AND s.sequence_schema = quote_ident(source_schema)
|
|
622
|
+
UNION
|
|
623
|
+
SELECT s.sequence_name::text,
|
|
624
|
+
g.groname as owner
|
|
625
|
+
FROM information_schema.sequences s
|
|
626
|
+
JOIN pg_class c ON (
|
|
627
|
+
s.sequence_name = c.relname
|
|
628
|
+
AND s.sequence_schema = c.relnamespace::regnamespace::text
|
|
629
|
+
)
|
|
630
|
+
JOIN pg_group g ON (c.relowner = g.grosysid)
|
|
631
|
+
WHERE c.relkind = 'S'
|
|
632
|
+
AND s.sequence_schema = quote_ident(source_schema) LOOP cnt := cnt + 1;
|
|
633
|
+
IF bDDLOnly THEN -- issue#95
|
|
634
|
+
RAISE INFO '%',
|
|
635
|
+
'CREATE SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object) || ';';
|
|
636
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
637
|
+
RAISE INFO '%',
|
|
638
|
+
'ALTER SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object) || ' OWNER TO ' || buffer || ';';
|
|
639
|
+
END IF;
|
|
640
|
+
ELSE EXECUTE 'CREATE SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object);
|
|
641
|
+
-- issue#95
|
|
642
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
643
|
+
EXECUTE 'ALTER SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object) || ' OWNER TO ' || buffer;
|
|
644
|
+
END IF;
|
|
645
|
+
END IF;
|
|
646
|
+
srctbl := quote_ident(source_schema) || '.' || quote_ident(object);
|
|
647
|
+
IF sq_server_version_num < 100000 THEN EXECUTE 'SELECT last_value, is_called FROM ' || quote_ident(source_schema) || '.' || quote_ident(object) || ';' INTO sq_last_value,
|
|
648
|
+
sq_is_called;
|
|
649
|
+
EXECUTE 'SELECT maximum_value, start_value, increment, minimum_value, 1 cache_size, cycle_option, data_type
|
|
650
|
+
FROM information_schema.sequences WHERE sequence_schema=' || quote_literal(source_schema) || ' AND sequence_name=' || quote_literal(object) || ';' INTO sq_max_value,
|
|
651
|
+
sq_start_value,
|
|
652
|
+
sq_increment_by,
|
|
653
|
+
sq_min_value,
|
|
654
|
+
sq_cache_value,
|
|
655
|
+
sq_is_cycled,
|
|
656
|
+
sq_data_type;
|
|
657
|
+
IF sq_is_cycled THEN sq_cycled := 'CYCLE';
|
|
658
|
+
ELSE sq_cycled := 'NO CYCLE';
|
|
659
|
+
END IF;
|
|
660
|
+
qry := 'ALTER SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object) || ' INCREMENT BY ' || sq_increment_by || ' MINVALUE ' || sq_min_value || ' MAXVALUE ' || sq_max_value -- will update current sequence value after this
|
|
661
|
+
|| ' START WITH ' || sq_start_value || ' RESTART ' || sq_min_value || ' CACHE ' || sq_cache_value || ' ' || sq_cycled || ' ;';
|
|
662
|
+
ELSE EXECUTE 'SELECT max_value, start_value, increment_by, min_value, cache_size, cycle, data_type, COALESCE(last_value, 1)
|
|
663
|
+
FROM pg_catalog.pg_sequences WHERE schemaname=' || quote_literal(source_schema) || ' AND sequencename=' || quote_literal(object) || ';' INTO sq_max_value,
|
|
664
|
+
sq_start_value,
|
|
665
|
+
sq_increment_by,
|
|
666
|
+
sq_min_value,
|
|
667
|
+
sq_cache_value,
|
|
668
|
+
sq_is_cycled,
|
|
669
|
+
sq_data_type,
|
|
670
|
+
sq_last_value;
|
|
671
|
+
IF sq_is_cycled THEN sq_cycled := 'CYCLE';
|
|
672
|
+
ELSE sq_cycled := 'NO CYCLE';
|
|
673
|
+
END IF;
|
|
674
|
+
qry := 'ALTER SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object) || ' AS ' || sq_data_type || ' INCREMENT BY ' || sq_increment_by || ' MINVALUE ' || sq_min_value || ' MAXVALUE ' || sq_max_value -- will update current sequence value after this
|
|
675
|
+
|| ' START WITH ' || sq_start_value || ' RESTART ' || sq_min_value || ' CACHE ' || sq_cache_value || ' ' || sq_cycled || ' ;';
|
|
676
|
+
END IF;
|
|
677
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
678
|
+
qry;
|
|
679
|
+
ELSE EXECUTE qry;
|
|
680
|
+
END IF;
|
|
681
|
+
buffer := quote_ident(dest_schema) || '.' || quote_ident(object);
|
|
682
|
+
IF bData THEN EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_last_value || ', ' || sq_is_called || ');';
|
|
683
|
+
ELSE if bDDLOnly THEN -- fix#63
|
|
684
|
+
-- RAISE INFO '%', 'SELECT setval( ''' || buffer || ''', ' || sq_start_value || ', ' || sq_is_called || ');' ;
|
|
685
|
+
RAISE INFO '%',
|
|
686
|
+
'SELECT setval( ''' || buffer || ''', ' || sq_last_value || ', ' || sq_is_called || ');';
|
|
687
|
+
ELSE -- fix#63
|
|
688
|
+
-- EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_start_value || ', ' || sq_is_called || ');' ;
|
|
689
|
+
EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_last_value || ', ' || sq_is_called || ');';
|
|
690
|
+
END IF;
|
|
691
|
+
END IF;
|
|
692
|
+
END LOOP;
|
|
693
|
+
RAISE NOTICE ' SEQUENCES cloned: %',
|
|
694
|
+
LPAD(cnt::text, 5, ' ');
|
|
695
|
+
-- Create tables including partitioned ones (parent/children) and unlogged ones. Order by is critical since child partition range logic is dependent on it.
|
|
696
|
+
action := 'Tables';
|
|
697
|
+
SELECT setting INTO v_dummy
|
|
698
|
+
FROM pg_settings
|
|
699
|
+
WHERE name = 'search_path';
|
|
700
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: search_path=%',
|
|
701
|
+
v_dummy;
|
|
702
|
+
END IF;
|
|
703
|
+
cnt := 0;
|
|
704
|
+
-- Issue#61 FIX: use set_config for empty string
|
|
705
|
+
-- SET search_path = '';
|
|
706
|
+
SELECT set_config('search_path', '', false) into v_dummy;
|
|
707
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: setting search_path to empty string:%',
|
|
708
|
+
v_dummy;
|
|
709
|
+
END IF;
|
|
710
|
+
-- Fix#86 add isgenerated to column list
|
|
711
|
+
-- Fix#91 add tblowner for setting the table ownership to that of the source
|
|
712
|
+
-- Fix#99 added join to pg_tablespace
|
|
713
|
+
-- Handle PG versions greater than last major/minor version of PG 9.6.24
|
|
714
|
+
IF sq_server_version_num > 90624 THEN FOR tblname,
|
|
715
|
+
relpersist,
|
|
716
|
+
bRelispart,
|
|
717
|
+
relknd,
|
|
718
|
+
data_type,
|
|
719
|
+
udt_name,
|
|
720
|
+
udt_schema,
|
|
721
|
+
ocomment,
|
|
722
|
+
l_child,
|
|
723
|
+
isGenerated,
|
|
724
|
+
tblowner,
|
|
725
|
+
tblspace IN -- 2021-03-08 MJV #39 fix: change sql to get indicator of user-defined columns to issue warnings
|
|
726
|
+
-- select c.relname, c.relpersistence, c.relispartition, c.relkind
|
|
727
|
+
-- FROM pg_class c, pg_namespace n where n.oid = c.relnamespace and n.nspname = quote_ident(source_schema) and c.relkind in ('r','p') and
|
|
728
|
+
-- order by c.relkind desc, c.relname
|
|
729
|
+
--Fix#65 add another left join to distinguish child tables by inheritance
|
|
730
|
+
-- Fix#86 add is_generated to column select
|
|
731
|
+
-- Fix#91 add tblowner to the select
|
|
732
|
+
-- Fix#105 need a different kinda distint to avoid retrieving a table twice in the case of a table with multiple USER-DEFINED datatypes using DISTINCT ON instead of just DISTINCT
|
|
733
|
+
--SELECT DISTINCT c.relname, c.relpersistence, c.relispartition, c.relkind, co.data_type, co.udt_name, co.udt_schema, obj_description(c.oid), i.inhrelid,
|
|
734
|
+
-- COALESCE(co.is_generated, ''), pg_catalog.pg_get_userbyid(c.relowner) as "Owner", CASE WHEN reltablespace = 0 THEN 'pg_default' ELSE ts.spcname END as tablespace
|
|
735
|
+
-- fixed #108 by enclosing owner in double quotes to avoid errors for bad characters like #.@...
|
|
736
|
+
-- SELECT DISTINCT ON (c.relname, c.relpersistence, c.relispartition, c.relkind, co.data_type) c.relname, c.relpersistence, c.relispartition, c.relkind, co.data_type, co.udt_name, co.udt_schema, obj_description(c.oid), i.inhrelid,
|
|
737
|
+
SELECT DISTINCT ON (
|
|
738
|
+
c.relname,
|
|
739
|
+
c.relpersistence,
|
|
740
|
+
c.relispartition,
|
|
741
|
+
c.relkind,
|
|
742
|
+
co.data_type
|
|
743
|
+
) c.relname,
|
|
744
|
+
c.relpersistence,
|
|
745
|
+
c.relispartition,
|
|
746
|
+
c.relkind,
|
|
747
|
+
co.data_type,
|
|
748
|
+
co.udt_name,
|
|
749
|
+
co.udt_schema,
|
|
750
|
+
obj_description(c.oid),
|
|
751
|
+
i.inhrelid,
|
|
752
|
+
COALESCE(co.is_generated, ''),
|
|
753
|
+
'"' || pg_catalog.pg_get_userbyid(c.relowner) || '"' as "Owner",
|
|
754
|
+
CASE
|
|
755
|
+
WHEN reltablespace = 0 THEN 'pg_default'
|
|
756
|
+
ELSE ts.spcname
|
|
757
|
+
END as tablespace
|
|
758
|
+
FROM pg_class c
|
|
759
|
+
JOIN pg_namespace n ON (
|
|
760
|
+
n.oid = c.relnamespace
|
|
761
|
+
AND n.nspname = quote_ident(source_schema)
|
|
762
|
+
AND c.relkind IN ('r', 'p')
|
|
763
|
+
)
|
|
764
|
+
LEFT JOIN information_schema.columns co ON (
|
|
765
|
+
co.table_schema = n.nspname
|
|
766
|
+
AND co.table_name = c.relname
|
|
767
|
+
AND (
|
|
768
|
+
co.data_type = 'USER-DEFINED'
|
|
769
|
+
OR co.is_generated = 'ALWAYS'
|
|
770
|
+
)
|
|
771
|
+
)
|
|
772
|
+
LEFT JOIN pg_inherits i ON (c.oid = i.inhrelid) -- issue#99 added join
|
|
773
|
+
LEFT JOIN pg_tablespace ts ON (c.reltablespace = ts.oid)
|
|
774
|
+
ORDER BY c.relkind DESC,
|
|
775
|
+
c.relname LOOP cnt := cnt + 1;
|
|
776
|
+
lastsql = '';
|
|
777
|
+
IF l_child IS NULL THEN bChild := False;
|
|
778
|
+
ELSE bChild := True;
|
|
779
|
+
END IF;
|
|
780
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: TABLE START --> table=% bRelispart=% relkind=% bChild=%',
|
|
781
|
+
tblname,
|
|
782
|
+
bRelispart,
|
|
783
|
+
relknd,
|
|
784
|
+
bChild;
|
|
785
|
+
END IF;
|
|
786
|
+
IF data_type = 'USER-DEFINED' THEN -- RAISE NOTICE ' Table (%) has column(s) with user-defined types so using get_table_ddl() instead of CREATE TABLE LIKE construct.',tblname;
|
|
787
|
+
cnt := cnt;
|
|
788
|
+
END IF;
|
|
789
|
+
buffer := quote_ident(dest_schema) || '.' || quote_ident(tblname);
|
|
790
|
+
buffer2 := '';
|
|
791
|
+
IF relpersist = 'u' THEN buffer2 := 'UNLOGGED ';
|
|
792
|
+
END IF;
|
|
793
|
+
IF relknd = 'r' THEN IF bDDLOnly THEN IF data_type = 'USER-DEFINED' THEN -- FIXED #65, #67
|
|
794
|
+
-- SELECT * INTO buffer3 FROM public.pg_get_tabledef(quote_ident(source_schema), tblname);
|
|
795
|
+
SELECT * INTO buffer3
|
|
796
|
+
FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
797
|
+
buffer3 := REPLACE(
|
|
798
|
+
buffer3,
|
|
799
|
+
quote_ident(source_schema) || '.',
|
|
800
|
+
quote_ident(dest_schema) || '.'
|
|
801
|
+
);
|
|
802
|
+
RAISE INFO '%',
|
|
803
|
+
buffer3;
|
|
804
|
+
-- issue#91 fix
|
|
805
|
+
-- issue#95
|
|
806
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
807
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % OWNER TO %;',
|
|
808
|
+
quote_ident(dest_schema) || '.' || tblname,
|
|
809
|
+
tblowner;
|
|
810
|
+
END IF;
|
|
811
|
+
ELSE IF NOT bChild THEN RAISE INFO '%',
|
|
812
|
+
'CREATE ' || buffer2 || 'TABLE ' || buffer || ' (LIKE ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ' INCLUDING ALL);';
|
|
813
|
+
-- issue#91 fix
|
|
814
|
+
-- issue#95
|
|
815
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
816
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % OWNER TO %;',
|
|
817
|
+
quote_ident(dest_schema) || '.' || tblname,
|
|
818
|
+
tblowner;
|
|
819
|
+
END IF;
|
|
820
|
+
-- issue#99
|
|
821
|
+
IF tblspace <> 'pg_default' THEN -- replace with user-defined tablespace
|
|
822
|
+
-- ALTER TABLE myschema.mytable SET TABLESPACE usrtblspc;
|
|
823
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % SET TABLESPACE %;',
|
|
824
|
+
quote_ident(dest_schema) || '.' || tblname,
|
|
825
|
+
tblspace;
|
|
826
|
+
END IF;
|
|
827
|
+
ELSE -- FIXED #65, #67
|
|
828
|
+
-- SELECT * INTO buffer3 FROM public.pg_get_tabledef(quote_ident(source_schema), tblname);
|
|
829
|
+
SELECT * INTO buffer3
|
|
830
|
+
FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
831
|
+
buffer3 := REPLACE(
|
|
832
|
+
buffer3,
|
|
833
|
+
quote_ident(source_schema) || '.',
|
|
834
|
+
quote_ident(dest_schema) || '.'
|
|
835
|
+
);
|
|
836
|
+
RAISE INFO '%',
|
|
837
|
+
buffer3;
|
|
838
|
+
-- issue#91 fix
|
|
839
|
+
-- issue#95
|
|
840
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
841
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % OWNER TO %;',
|
|
842
|
+
quote_ident(dest_schema) || '.' || tblname,
|
|
843
|
+
tblowner;
|
|
844
|
+
END IF;
|
|
845
|
+
END IF;
|
|
846
|
+
END IF;
|
|
847
|
+
ELSE IF data_type = 'USER-DEFINED' THEN -- FIXED #65, #67
|
|
848
|
+
-- SELECT * INTO buffer3 FROM public.pg_get_tabledef(quote_ident(source_schema), tblname);
|
|
849
|
+
SELECT * INTO buffer3
|
|
850
|
+
FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
851
|
+
buffer3 := REPLACE(
|
|
852
|
+
buffer3,
|
|
853
|
+
quote_ident(source_schema) || '.',
|
|
854
|
+
quote_ident(dest_schema) || '.'
|
|
855
|
+
);
|
|
856
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef01:%',
|
|
857
|
+
buffer3;
|
|
858
|
+
END IF;
|
|
859
|
+
-- #82: Table def should be fully qualified with target schema,
|
|
860
|
+
-- so just make search path = public to handle extension types that should reside in public schema
|
|
861
|
+
v_dummy = 'public';
|
|
862
|
+
SELECT set_config('search_path', v_dummy, false) into v_dummy;
|
|
863
|
+
EXECUTE buffer3;
|
|
864
|
+
-- issue#91 fix
|
|
865
|
+
-- issue#95
|
|
866
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
867
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || tblname || ' OWNER TO ' || tblowner;
|
|
868
|
+
lastsql = buffer3;
|
|
869
|
+
EXECUTE buffer3;
|
|
870
|
+
END IF;
|
|
871
|
+
ELSE IF (
|
|
872
|
+
NOT bChild
|
|
873
|
+
OR bRelispart
|
|
874
|
+
) THEN buffer3 := 'CREATE ' || buffer2 || 'TABLE ' || buffer || ' (LIKE ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ' INCLUDING ALL)';
|
|
875
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef02:%',
|
|
876
|
+
buffer3;
|
|
877
|
+
END IF;
|
|
878
|
+
EXECUTE buffer3;
|
|
879
|
+
-- issue#91 fix
|
|
880
|
+
-- issue#95
|
|
881
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
882
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' OWNER TO ' || tblowner;
|
|
883
|
+
lastsql = buffer3;
|
|
884
|
+
EXECUTE buffer3;
|
|
885
|
+
END IF;
|
|
886
|
+
-- issue#99
|
|
887
|
+
IF tblspace <> 'pg_default' THEN -- replace with user-defined tablespace
|
|
888
|
+
-- ALTER TABLE myschema.mytable SET TABLESPACE usrtblspc;
|
|
889
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || tblname || ' SET TABLESPACE ' || tblspace;
|
|
890
|
+
EXECUTE buffer3;
|
|
891
|
+
END IF;
|
|
892
|
+
ELSE -- FIXED #65, #67
|
|
893
|
+
-- SELECT * INTO buffer3 FROM public.pg_get_tabledef(quote_ident(source_schema), tblname);
|
|
894
|
+
SELECT * INTO buffer3
|
|
895
|
+
FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
896
|
+
buffer3 := REPLACE(
|
|
897
|
+
buffer3,
|
|
898
|
+
quote_ident(source_schema) || '.',
|
|
899
|
+
quote_ident(dest_schema) || '.'
|
|
900
|
+
);
|
|
901
|
+
-- set client_min_messages higher to avoid messages like this:
|
|
902
|
+
-- NOTICE: merging column "city_id" with inherited definition
|
|
903
|
+
set client_min_messages = 'WARNING';
|
|
904
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef03:%',
|
|
905
|
+
buffer3;
|
|
906
|
+
END IF;
|
|
907
|
+
EXECUTE buffer3;
|
|
908
|
+
-- issue#91 fix
|
|
909
|
+
-- issue#95
|
|
910
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
911
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || tblname || ' OWNER TO ' || tblowner;
|
|
912
|
+
lastsql = buffer3;
|
|
913
|
+
EXECUTE buffer3;
|
|
914
|
+
END IF;
|
|
915
|
+
-- reset it back, only get these for inheritance-based tables
|
|
916
|
+
set client_min_messages = 'notice';
|
|
917
|
+
END IF;
|
|
918
|
+
END IF;
|
|
919
|
+
-- Add table comment.
|
|
920
|
+
IF ocomment IS NOT NULL THEN EXECUTE 'COMMENT ON TABLE ' || buffer || ' IS ' || quote_literal(ocomment);
|
|
921
|
+
END IF;
|
|
922
|
+
END IF;
|
|
923
|
+
ELSIF relknd = 'p' THEN -- define parent table and assume child tables have already been created based on top level sort order.
|
|
924
|
+
-- Issue #103 Put the complex query into its own function, get_table_ddl_complex()
|
|
925
|
+
SELECT * INTO qry
|
|
926
|
+
FROM public.get_table_ddl_complex(
|
|
927
|
+
source_schema,
|
|
928
|
+
dest_schema,
|
|
929
|
+
tblname,
|
|
930
|
+
sq_server_version_num
|
|
931
|
+
);
|
|
932
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef04 - %',
|
|
933
|
+
buffer;
|
|
934
|
+
END IF;
|
|
935
|
+
-- consider replacing complicated query above with this simple call to get_table_ddl()...
|
|
936
|
+
-- SELECT * INTO qry FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
937
|
+
-- qry := REPLACE(qry, quote_ident(source_schema) || '.', quote_ident(dest_schema) || '.');
|
|
938
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
939
|
+
qry;
|
|
940
|
+
-- issue#95
|
|
941
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
942
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % OWNER TO %;',
|
|
943
|
+
quote_ident(dest_schema) || '.' || quote_ident(tblname),
|
|
944
|
+
tblowner;
|
|
945
|
+
END IF;
|
|
946
|
+
ELSE -- Issue#103: we need to always set search_path priority to target schema when we execute DDL
|
|
947
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef04 context: old search path=% new search path=% current search path=%',
|
|
948
|
+
src_path_old,
|
|
949
|
+
src_path_new,
|
|
950
|
+
v_dummy;
|
|
951
|
+
END IF;
|
|
952
|
+
SELECT setting INTO spath_tmp
|
|
953
|
+
FROM pg_settings
|
|
954
|
+
WHERE name = 'search_path';
|
|
955
|
+
IF spath_tmp <> dest_schema THEN -- change it to target schema and don't forget to change it back after we execute the DDL
|
|
956
|
+
spath = 'SET search_path = "' || dest_schema || '"';
|
|
957
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: changing search_path --> %',
|
|
958
|
+
spath;
|
|
959
|
+
END IF;
|
|
960
|
+
EXECUTE spath;
|
|
961
|
+
SELECT setting INTO v_dummy
|
|
962
|
+
FROM pg_settings
|
|
963
|
+
WHERE name = 'search_path';
|
|
964
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: search_path changed to %',
|
|
965
|
+
v_dummy;
|
|
966
|
+
END IF;
|
|
967
|
+
END IF;
|
|
968
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef04:%',
|
|
969
|
+
qry;
|
|
970
|
+
END IF;
|
|
971
|
+
EXECUTE qry;
|
|
972
|
+
-- Issue#103
|
|
973
|
+
-- Set search path back to what it was
|
|
974
|
+
spath = 'SET search_path = "' || spath_tmp || '"';
|
|
975
|
+
EXECUTE spath;
|
|
976
|
+
SELECT setting INTO v_dummy
|
|
977
|
+
FROM pg_settings
|
|
978
|
+
WHERE name = 'search_path';
|
|
979
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: search_path changed back to %',
|
|
980
|
+
v_dummy;
|
|
981
|
+
END IF;
|
|
982
|
+
-- issue#91 fix
|
|
983
|
+
-- issue#95
|
|
984
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
985
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' OWNER TO ' || tblowner;
|
|
986
|
+
lastsql = buffer3;
|
|
987
|
+
EXECUTE buffer3;
|
|
988
|
+
END IF;
|
|
989
|
+
END IF;
|
|
990
|
+
-- loop for child tables and alter them to attach to parent for specific partition method.
|
|
991
|
+
-- Issue#103 fix: only loop for the table we are currently processing, tblname!
|
|
992
|
+
FOR aname,
|
|
993
|
+
part_range,
|
|
994
|
+
object IN
|
|
995
|
+
SELECT quote_ident(dest_schema) || '.' || c1.relname as tablename,
|
|
996
|
+
pg_catalog.pg_get_expr(c1.relpartbound, c1.oid) as partrange,
|
|
997
|
+
quote_ident(dest_schema) || '.' || c2.relname as object
|
|
998
|
+
FROM pg_catalog.pg_class c1,
|
|
999
|
+
pg_namespace n,
|
|
1000
|
+
pg_catalog.pg_inherits i,
|
|
1001
|
+
pg_class c2
|
|
1002
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
1003
|
+
AND c1.relnamespace = n.oid
|
|
1004
|
+
AND c1.relkind = 'r' -- Issue#103: added this condition to only work on current partitioned table. The problem was regression testing previously only worked on one partition table clone case
|
|
1005
|
+
AND c2.relname = tblname
|
|
1006
|
+
AND c1.relispartition
|
|
1007
|
+
AND c1.oid = i.inhrelid
|
|
1008
|
+
AND i.inhparent = c2.oid
|
|
1009
|
+
AND c2.relnamespace = n.oid
|
|
1010
|
+
ORDER BY pg_catalog.pg_get_expr(c1.relpartbound, c1.oid) = 'DEFAULT',
|
|
1011
|
+
c1.oid::pg_catalog.regclass::pg_catalog.text LOOP qry := 'ALTER TABLE ONLY ' || object || ' ATTACH PARTITION ' || aname || ' ' || part_range || ';';
|
|
1012
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: %',
|
|
1013
|
+
qry;
|
|
1014
|
+
END IF;
|
|
1015
|
+
-- issue#91, not sure if we need to do this for child tables
|
|
1016
|
+
-- issue#95 we dont set ownership here
|
|
1017
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
1018
|
+
qry;
|
|
1019
|
+
IF NOT bNoOwner THEN NULL;
|
|
1020
|
+
END IF;
|
|
1021
|
+
ELSE EXECUTE qry;
|
|
1022
|
+
IF NOT bNoOwner THEN NULL;
|
|
1023
|
+
END IF;
|
|
1024
|
+
END IF;
|
|
1025
|
+
END LOOP;
|
|
1026
|
+
END IF;
|
|
1027
|
+
-- INCLUDING ALL creates new index names, we restore them to the old name.
|
|
1028
|
+
-- There should be no conflicts since they live in different schemas
|
|
1029
|
+
FOR ix_old_name,
|
|
1030
|
+
ix_new_name IN
|
|
1031
|
+
SELECT old.indexname,
|
|
1032
|
+
new.indexname
|
|
1033
|
+
FROM pg_indexes old,
|
|
1034
|
+
pg_indexes new
|
|
1035
|
+
WHERE old.schemaname = source_schema
|
|
1036
|
+
AND new.schemaname = dest_schema
|
|
1037
|
+
AND old.tablename = new.tablename
|
|
1038
|
+
AND old.tablename = tblname
|
|
1039
|
+
AND old.indexname <> new.indexname
|
|
1040
|
+
AND regexp_replace(old.indexdef, E'.*USING', '') = regexp_replace(new.indexdef, E'.*USING', '')
|
|
1041
|
+
ORDER BY old.indexdef,
|
|
1042
|
+
new.indexdef LOOP IF bDDLOnly THEN RAISE INFO '%',
|
|
1043
|
+
'ALTER INDEX ' || quote_ident(dest_schema) || '.' || quote_ident(ix_new_name) || ' RENAME TO ' || quote_ident(ix_old_name) || ';';
|
|
1044
|
+
ELSE -- The SELECT query above may return duplicate names when a column is
|
|
1045
|
+
-- indexed twice the same manner with 2 different names. Therefore, to
|
|
1046
|
+
-- avoid a 'relation "xxx" already exists' we test if the index name
|
|
1047
|
+
-- is in use or free. Skipping existing index will fallback on unused
|
|
1048
|
+
-- ones and every duplicate will be mapped to distinct old names.
|
|
1049
|
+
IF NOT EXISTS (
|
|
1050
|
+
SELECT TRUE
|
|
1051
|
+
FROM pg_indexes
|
|
1052
|
+
WHERE schemaname = dest_schema
|
|
1053
|
+
AND tablename = tblname
|
|
1054
|
+
AND indexname = quote_ident(ix_old_name)
|
|
1055
|
+
)
|
|
1056
|
+
AND EXISTS (
|
|
1057
|
+
SELECT TRUE
|
|
1058
|
+
FROM pg_indexes
|
|
1059
|
+
WHERE schemaname = dest_schema
|
|
1060
|
+
AND tablename = tblname
|
|
1061
|
+
AND indexname = quote_ident(ix_new_name)
|
|
1062
|
+
) THEN EXECUTE 'ALTER INDEX ' || quote_ident(dest_schema) || '.' || quote_ident(ix_new_name) || ' RENAME TO ' || quote_ident(ix_old_name) || ';';
|
|
1063
|
+
END IF;
|
|
1064
|
+
END IF;
|
|
1065
|
+
END LOOP;
|
|
1066
|
+
lastsql = '';
|
|
1067
|
+
IF bData THEN -- Insert records from source table
|
|
1068
|
+
-- 2021-03-03 MJV FIX
|
|
1069
|
+
buffer := dest_schema || '.' || quote_ident(tblname);
|
|
1070
|
+
-- 2020/06/18 - Issue #31 fix: add "OVERRIDING SYSTEM VALUE" for IDENTITY columns marked as GENERATED ALWAYS.
|
|
1071
|
+
select count(*) into cnt2
|
|
1072
|
+
from pg_class c,
|
|
1073
|
+
pg_attribute a,
|
|
1074
|
+
pg_namespace n
|
|
1075
|
+
where a.attrelid = c.oid
|
|
1076
|
+
and c.relname = quote_ident(tblname)
|
|
1077
|
+
and n.oid = c.relnamespace
|
|
1078
|
+
and n.nspname = quote_ident(source_schema)
|
|
1079
|
+
and a.attidentity = 'a';
|
|
1080
|
+
buffer3 := '';
|
|
1081
|
+
IF cnt2 > 0 THEN buffer3 := ' OVERRIDING SYSTEM VALUE';
|
|
1082
|
+
END IF;
|
|
1083
|
+
-- BUG for inserting rows from tables with user-defined columns
|
|
1084
|
+
-- INSERT INTO sample_clone.address OVERRIDING SYSTEM VALUE SELECT * FROM sample.address;
|
|
1085
|
+
-- ERROR: column "id2" is of type sample_clone.udt_myint but expression is of type udt_myint
|
|
1086
|
+
-- Issue#86 fix:
|
|
1087
|
+
-- IF data_type = 'USER-DEFINED' THEN
|
|
1088
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: includerecs branch table=% data_type=% isgenerated=% buffer3=%',
|
|
1089
|
+
tblname,
|
|
1090
|
+
data_type,
|
|
1091
|
+
isGenerated,
|
|
1092
|
+
buffer3;
|
|
1093
|
+
END IF;
|
|
1094
|
+
IF data_type = 'USER-DEFINED'
|
|
1095
|
+
OR isGenerated = 'ALWAYS' THEN -- RAISE WARNING 'Bypassing copying rows for table (%) with user-defined data types. You must copy them manually.', tblname;
|
|
1096
|
+
-- wont work --> INSERT INTO clone1.address (id2, id3, addr) SELECT cast(id2 as clone1.udt_myint), cast(id3 as clone1.udt_myint), addr FROM sample.address;
|
|
1097
|
+
-- Issue#101 --> INSERT INTO clone1.address2 (id2, id3, addr) SELECT id2::text::clone1.udt_myint, id3::text::clone1.udt_myint, addr FROM sample.address;
|
|
1098
|
+
-- Issue#79 implementation follows
|
|
1099
|
+
-- COPY sample.statuses(id, s) TO '/tmp/statuses.txt' WITH DELIMITER AS ',';
|
|
1100
|
+
-- COPY sample_clone1.statuses FROM '/tmp/statuses.txt' (DELIMITER ',', NULL '');
|
|
1101
|
+
-- Issue#101 fix: use text cast to get around the problem.
|
|
1102
|
+
IF bFileCopy THEN IF bWindows THEN buffer2 := 'COPY ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ' TO ''C:\WINDOWS\TEMP\cloneschema.tmp'' WITH DELIMITER AS '','';';
|
|
1103
|
+
tblarray2 := tblarray2 || buffer2;
|
|
1104
|
+
-- Issue #81 reformat COPY command for upload
|
|
1105
|
+
-- buffer2:= 'COPY ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' FROM ''C:\WINDOWS\TEMP\cloneschema.tmp'' (DELIMITER '','', NULL '''');';
|
|
1106
|
+
buffer2 := 'COPY ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' FROM ''C:\WINDOWS\TEMP\cloneschema.tmp'' (DELIMITER '','', NULL ''\N'', FORMAT CSV);';
|
|
1107
|
+
tblarray2 := tblarray2 || buffer2;
|
|
1108
|
+
ELSE buffer2 := 'COPY ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ' TO ''/tmp/cloneschema.tmp'' WITH DELIMITER AS '','';';
|
|
1109
|
+
tblarray2 := tblarray2 || buffer2;
|
|
1110
|
+
-- Issue #81 reformat COPY command for upload
|
|
1111
|
+
-- buffer2 := 'COPY ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' FROM ''/tmp/cloneschema.tmp'' (DELIMITER '','', NULL '''');';
|
|
1112
|
+
-- works--> COPY sample.timestamptbl2 FROM '/tmp/cloneschema.tmp' WITH (DELIMITER ',', NULL '\N', FORMAT CSV) ;
|
|
1113
|
+
buffer2 := 'COPY ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' FROM ''/tmp/cloneschema.tmp'' (DELIMITER '','', NULL ''\N'', FORMAT CSV);';
|
|
1114
|
+
tblarray2 := tblarray2 || buffer2;
|
|
1115
|
+
END IF;
|
|
1116
|
+
ELSE -- Issue#101: assume direct copy with text cast, add to separate array
|
|
1117
|
+
SELECT * INTO buffer3
|
|
1118
|
+
FROM public.get_insert_stmt_ddl(
|
|
1119
|
+
quote_ident(source_schema),
|
|
1120
|
+
quote_ident(dest_schema),
|
|
1121
|
+
quote_ident(tblname),
|
|
1122
|
+
True
|
|
1123
|
+
);
|
|
1124
|
+
tblarray3 := tblarray3 || buffer3;
|
|
1125
|
+
END IF;
|
|
1126
|
+
ELSE -- bypass child tables since we populate them when we populate the parents
|
|
1127
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tblname=% bRelispart=% relknd=% l_child=% bChild=%',
|
|
1128
|
+
tblname,
|
|
1129
|
+
bRelispart,
|
|
1130
|
+
relknd,
|
|
1131
|
+
l_child,
|
|
1132
|
+
bChild;
|
|
1133
|
+
END IF;
|
|
1134
|
+
IF NOT bRelispart
|
|
1135
|
+
AND NOT bChild THEN -- Issue#75: Must defer population of tables until child tables have been added to parents
|
|
1136
|
+
-- Issue#101 Offer alternative of copy to/from file. Although originally intended for tables with UDTs, it is now expanded to handle all cases for performance improvement perhaps for large tables.
|
|
1137
|
+
-- Issue#106 buffer3 shouldnt be in the mix
|
|
1138
|
+
-- revisited: buffer3 should be in play for PG versions that handle IDENTITIES
|
|
1139
|
+
buffer2 := 'INSERT INTO ' || buffer || buffer3 || ' SELECT * FROM ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ';';
|
|
1140
|
+
-- buffer2 := 'INSERT INTO ' || buffer || ' SELECT * FROM ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ';';
|
|
1141
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: buffer2=%',
|
|
1142
|
+
buffer2;
|
|
1143
|
+
END IF;
|
|
1144
|
+
IF bFileCopy THEN tblarray2 := tblarray2 || buffer2;
|
|
1145
|
+
ELSE tblarray := tblarray || buffer2;
|
|
1146
|
+
END IF;
|
|
1147
|
+
END IF;
|
|
1148
|
+
END IF;
|
|
1149
|
+
END IF;
|
|
1150
|
+
-- Issue#61 FIX: use set_config for empty string
|
|
1151
|
+
-- SET search_path = '';
|
|
1152
|
+
SELECT set_config('search_path', '', false) into v_dummy;
|
|
1153
|
+
FOR column_,
|
|
1154
|
+
default_ IN
|
|
1155
|
+
SELECT column_name::text,
|
|
1156
|
+
REPLACE(
|
|
1157
|
+
column_default::text,
|
|
1158
|
+
quote_ident(source_schema) || '.',
|
|
1159
|
+
quote_ident(dest_schema) || '.'
|
|
1160
|
+
)
|
|
1161
|
+
FROM information_schema.COLUMNS
|
|
1162
|
+
WHERE table_schema = source_schema
|
|
1163
|
+
AND TABLE_NAME = tblname
|
|
1164
|
+
AND column_default LIKE 'nextval(%' || quote_ident(source_schema) || '%::regclass)' LOOP -- Issue#78 FIX: handle case-sensitive names with quote_ident() on column name
|
|
1165
|
+
buffer2 = 'ALTER TABLE ' || buffer || ' ALTER COLUMN ' || quote_ident(column_) || ' SET DEFAULT ' || default_ || ';';
|
|
1166
|
+
IF bDDLOnly THEN -- May need to come back and revisit this since previous sql will not return anything since no schema as created!
|
|
1167
|
+
RAISE INFO '%',
|
|
1168
|
+
buffer2;
|
|
1169
|
+
ELSE EXECUTE buffer2;
|
|
1170
|
+
END IF;
|
|
1171
|
+
END LOOP;
|
|
1172
|
+
EXECUTE 'SET search_path = ' || quote_ident(source_schema);
|
|
1173
|
+
END LOOP;
|
|
1174
|
+
ELSE -- Handle 9.6 versions 90600
|
|
1175
|
+
FOR tblname,
|
|
1176
|
+
relpersist,
|
|
1177
|
+
relknd,
|
|
1178
|
+
data_type,
|
|
1179
|
+
udt_name,
|
|
1180
|
+
udt_schema,
|
|
1181
|
+
ocomment,
|
|
1182
|
+
l_child,
|
|
1183
|
+
isGenerated,
|
|
1184
|
+
tblowner,
|
|
1185
|
+
tblspace IN -- 2021-03-08 MJV #39 fix: change sql to get indicator of user-defined columns to issue warnings
|
|
1186
|
+
-- select c.relname, c.relpersistence, c.relispartition, c.relkind
|
|
1187
|
+
-- FROM pg_class c, pg_namespace n where n.oid = c.relnamespace and n.nspname = quote_ident(source_schema) and c.relkind in ('r','p') and
|
|
1188
|
+
-- order by c.relkind desc, c.relname
|
|
1189
|
+
--Fix#65 add another left join to distinguish child tables by inheritance
|
|
1190
|
+
-- Fix#86 add is_generated to column select
|
|
1191
|
+
-- Fix#91 add tblowner to the select
|
|
1192
|
+
-- Fix#105 need a different kinda distint to avoid retrieving a table twice in the case of a table with multiple USER-DEFINED datatypes using DISTINCT ON instead of just DISTINCT
|
|
1193
|
+
-- Fixed Issue#108: double quote roles to avoid problems with special characters in OWNER TO statements
|
|
1194
|
+
--SELECT DISTINCT c.relname, c.relpersistence, c.relispartition, c.relkind, co.data_type, co.udt_name, co.udt_schema, obj_description(c.oid), i.inhrelid,
|
|
1195
|
+
-- COALESCE(co.is_generated, ''), pg_catalog.pg_get_userbyid(c.relowner) as "Owner", CASE WHEN reltablespace = 0 THEN 'pg_default' ELSE ts.spcname END as tablespace
|
|
1196
|
+
-- SELECT DISTINCT ON (c.relname, c.relpersistence, c.relkind, co.data_type) c.relname, c.relpersistence, c.relkind, co.data_type, co.udt_name, co.udt_schema, obj_description(c.oid), i.inhrelid,
|
|
1197
|
+
-- COALESCE(co.is_generated, ''), pg_catalog.pg_get_userbyid(c.relowner) as "Owner", CASE WHEN reltablespace = 0 THEN 'pg_default' ELSE ts.spcname END as tablespace
|
|
1198
|
+
SELECT DISTINCT ON (
|
|
1199
|
+
c.relname,
|
|
1200
|
+
c.relpersistence,
|
|
1201
|
+
c.relkind,
|
|
1202
|
+
co.data_type
|
|
1203
|
+
) c.relname,
|
|
1204
|
+
c.relpersistence,
|
|
1205
|
+
c.relkind,
|
|
1206
|
+
co.data_type,
|
|
1207
|
+
co.udt_name,
|
|
1208
|
+
co.udt_schema,
|
|
1209
|
+
obj_description(c.oid),
|
|
1210
|
+
i.inhrelid,
|
|
1211
|
+
COALESCE(co.is_generated, ''),
|
|
1212
|
+
'"' || pg_catalog.pg_get_userbyid(c.relowner) || '"' as "Owner",
|
|
1213
|
+
CASE
|
|
1214
|
+
WHEN reltablespace = 0 THEN 'pg_default'
|
|
1215
|
+
ELSE ts.spcname
|
|
1216
|
+
END as tablespace
|
|
1217
|
+
FROM pg_class c
|
|
1218
|
+
JOIN pg_namespace n ON (
|
|
1219
|
+
n.oid = c.relnamespace
|
|
1220
|
+
AND n.nspname = quote_ident(source_schema)
|
|
1221
|
+
AND c.relkind IN ('r', 'p')
|
|
1222
|
+
)
|
|
1223
|
+
LEFT JOIN information_schema.columns co ON (
|
|
1224
|
+
co.table_schema = n.nspname
|
|
1225
|
+
AND co.table_name = c.relname
|
|
1226
|
+
AND (
|
|
1227
|
+
co.data_type = 'USER-DEFINED'
|
|
1228
|
+
OR co.is_generated = 'ALWAYS'
|
|
1229
|
+
)
|
|
1230
|
+
)
|
|
1231
|
+
LEFT JOIN pg_inherits i ON (c.oid = i.inhrelid) -- issue#99 added join
|
|
1232
|
+
LEFT JOIN pg_tablespace ts ON (c.reltablespace = ts.oid)
|
|
1233
|
+
ORDER BY c.relkind DESC,
|
|
1234
|
+
c.relname LOOP cnt := cnt + 1;
|
|
1235
|
+
IF l_child IS NULL THEN bChild := False;
|
|
1236
|
+
ELSE bChild := True;
|
|
1237
|
+
END IF;
|
|
1238
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: TABLE START --> table=% bRelispart=NA relkind=% bChild=%',
|
|
1239
|
+
tblname,
|
|
1240
|
+
relknd,
|
|
1241
|
+
bChild;
|
|
1242
|
+
END IF;
|
|
1243
|
+
IF data_type = 'USER-DEFINED' THEN -- RAISE NOTICE ' Table (%) has column(s) with user-defined types so using get_table_ddl() instead of CREATE TABLE LIKE construct.',tblname;
|
|
1244
|
+
cnt := cnt;
|
|
1245
|
+
END IF;
|
|
1246
|
+
buffer := quote_ident(dest_schema) || '.' || quote_ident(tblname);
|
|
1247
|
+
buffer2 := '';
|
|
1248
|
+
IF relpersist = 'u' THEN buffer2 := 'UNLOGGED ';
|
|
1249
|
+
END IF;
|
|
1250
|
+
IF relknd = 'r' THEN IF bDDLOnly THEN IF data_type = 'USER-DEFINED' THEN -- FIXED #65, #67
|
|
1251
|
+
-- SELECT * INTO buffer3 FROM public.pg_get_tabledef(quote_ident(source_schema), tblname);
|
|
1252
|
+
SELECT * INTO buffer3
|
|
1253
|
+
FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
1254
|
+
buffer3 := REPLACE(
|
|
1255
|
+
buffer3,
|
|
1256
|
+
quote_ident(source_schema) || '.',
|
|
1257
|
+
quote_ident(dest_schema) || '.'
|
|
1258
|
+
);
|
|
1259
|
+
RAISE INFO '%',
|
|
1260
|
+
buffer3;
|
|
1261
|
+
-- issue#91 fix
|
|
1262
|
+
-- issue#95
|
|
1263
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1264
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % OWNER TO %;',
|
|
1265
|
+
quote_ident(dest_schema) || '.' || tblname,
|
|
1266
|
+
tblowner;
|
|
1267
|
+
END IF;
|
|
1268
|
+
ELSE IF NOT bChild THEN RAISE INFO '%',
|
|
1269
|
+
'CREATE ' || buffer2 || 'TABLE ' || buffer || ' (LIKE ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ' INCLUDING ALL);';
|
|
1270
|
+
-- issue#91 fix
|
|
1271
|
+
-- issue#95
|
|
1272
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1273
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % OWNER TO %;',
|
|
1274
|
+
quote_ident(dest_schema) || '.' || tblname,
|
|
1275
|
+
tblowner;
|
|
1276
|
+
END IF;
|
|
1277
|
+
-- issue#99
|
|
1278
|
+
IF tblspace <> 'pg_default' THEN -- replace with user-defined tablespace
|
|
1279
|
+
-- ALTER TABLE myschema.mytable SET TABLESPACE usrtblspc;
|
|
1280
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % SET TABLESPACE %;',
|
|
1281
|
+
quote_ident(dest_schema) || '.' || tblname,
|
|
1282
|
+
tblspace;
|
|
1283
|
+
END IF;
|
|
1284
|
+
ELSE -- FIXED #65, #67
|
|
1285
|
+
-- SELECT * INTO buffer3 FROM public.pg_get_tabledef(quote_ident(source_schema), tblname);
|
|
1286
|
+
SELECT * INTO buffer3
|
|
1287
|
+
FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
1288
|
+
buffer3 := REPLACE(
|
|
1289
|
+
buffer3,
|
|
1290
|
+
quote_ident(source_schema) || '.',
|
|
1291
|
+
quote_ident(dest_schema) || '.'
|
|
1292
|
+
);
|
|
1293
|
+
RAISE INFO '%',
|
|
1294
|
+
buffer3;
|
|
1295
|
+
-- issue#91 fix
|
|
1296
|
+
-- issue#95
|
|
1297
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1298
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % OWNER TO %;',
|
|
1299
|
+
quote_ident(dest_schema) || '.' || tblname,
|
|
1300
|
+
tblowner;
|
|
1301
|
+
END IF;
|
|
1302
|
+
END IF;
|
|
1303
|
+
END IF;
|
|
1304
|
+
ELSE IF data_type = 'USER-DEFINED' THEN -- FIXED #65, #67
|
|
1305
|
+
-- SELECT * INTO buffer3 FROM public.pg_get_tabledef(quote_ident(source_schema), tblname);
|
|
1306
|
+
SELECT * INTO buffer3
|
|
1307
|
+
FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
1308
|
+
buffer3 := REPLACE(
|
|
1309
|
+
buffer3,
|
|
1310
|
+
quote_ident(source_schema) || '.',
|
|
1311
|
+
quote_ident(dest_schema) || '.'
|
|
1312
|
+
);
|
|
1313
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef01:%',
|
|
1314
|
+
buffer3;
|
|
1315
|
+
END IF;
|
|
1316
|
+
-- #82: Table def should be fully qualified with target schema,
|
|
1317
|
+
-- so just make search path = public to handle extension types that should reside in public schema
|
|
1318
|
+
v_dummy = 'public';
|
|
1319
|
+
SELECT set_config('search_path', v_dummy, false) into v_dummy;
|
|
1320
|
+
EXECUTE buffer3;
|
|
1321
|
+
-- issue#91 fix
|
|
1322
|
+
-- issue#95
|
|
1323
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1324
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || tblname || ' OWNER TO ' || tblowner;
|
|
1325
|
+
lastsql = buffer3;
|
|
1326
|
+
EXECUTE buffer3;
|
|
1327
|
+
END IF;
|
|
1328
|
+
ELSE IF (NOT bChild) THEN buffer3 := 'CREATE ' || buffer2 || 'TABLE ' || buffer || ' (LIKE ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ' INCLUDING ALL)';
|
|
1329
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef02:%',
|
|
1330
|
+
buffer3;
|
|
1331
|
+
END IF;
|
|
1332
|
+
EXECUTE buffer3;
|
|
1333
|
+
-- issue#91 fix
|
|
1334
|
+
-- issue#95
|
|
1335
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1336
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' OWNER TO ' || tblowner;
|
|
1337
|
+
lastsql = buffer3;
|
|
1338
|
+
EXECUTE buffer3;
|
|
1339
|
+
END IF;
|
|
1340
|
+
-- issue#99
|
|
1341
|
+
IF tblspace <> 'pg_default' THEN -- replace with user-defined tablespace
|
|
1342
|
+
-- ALTER TABLE myschema.mytable SET TABLESPACE usrtblspc;
|
|
1343
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || tblname || ' SET TABLESPACE ' || tblspace;
|
|
1344
|
+
EXECUTE buffer3;
|
|
1345
|
+
END IF;
|
|
1346
|
+
ELSE -- FIXED #65, #67
|
|
1347
|
+
-- SELECT * INTO buffer3 FROM public.pg_get_tabledef(quote_ident(source_schema), tblname);
|
|
1348
|
+
SELECT * INTO buffer3
|
|
1349
|
+
FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
1350
|
+
buffer3 := REPLACE(
|
|
1351
|
+
buffer3,
|
|
1352
|
+
quote_ident(source_schema) || '.',
|
|
1353
|
+
quote_ident(dest_schema) || '.'
|
|
1354
|
+
);
|
|
1355
|
+
-- set client_min_messages higher to avoid messages like this:
|
|
1356
|
+
-- NOTICE: merging column "city_id" with inherited definition
|
|
1357
|
+
set client_min_messages = 'WARNING';
|
|
1358
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef03:%',
|
|
1359
|
+
buffer3;
|
|
1360
|
+
END IF;
|
|
1361
|
+
EXECUTE buffer3;
|
|
1362
|
+
-- issue#91 fix
|
|
1363
|
+
-- issue#95
|
|
1364
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1365
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || tblname || ' OWNER TO ' || tblowner;
|
|
1366
|
+
lastsql = buffer3;
|
|
1367
|
+
EXECUTE buffer3;
|
|
1368
|
+
END IF;
|
|
1369
|
+
-- reset it back, only get these for inheritance-based tables
|
|
1370
|
+
set client_min_messages = 'notice';
|
|
1371
|
+
END IF;
|
|
1372
|
+
END IF;
|
|
1373
|
+
-- Add table comment.
|
|
1374
|
+
IF ocomment IS NOT NULL THEN EXECUTE 'COMMENT ON TABLE ' || buffer || ' IS ' || quote_literal(ocomment);
|
|
1375
|
+
END IF;
|
|
1376
|
+
END IF;
|
|
1377
|
+
ELSIF relknd = 'p' THEN -- define parent table and assume child tables have already been created based on top level sort order.
|
|
1378
|
+
-- Issue #103 Put the complex query into its own function, get_table_ddl_complex()
|
|
1379
|
+
SELECT * INTO qry
|
|
1380
|
+
FROM public.get_table_ddl_complex(
|
|
1381
|
+
source_schema,
|
|
1382
|
+
dest_schema,
|
|
1383
|
+
tblname,
|
|
1384
|
+
sq_server_version_num
|
|
1385
|
+
);
|
|
1386
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef04 - %',
|
|
1387
|
+
buffer;
|
|
1388
|
+
END IF;
|
|
1389
|
+
-- consider replacing complicated query above with this simple call to get_table_ddl()...
|
|
1390
|
+
-- SELECT * INTO qry FROM public.get_table_ddl(quote_ident(source_schema), tblname, False);
|
|
1391
|
+
-- qry := REPLACE(qry, quote_ident(source_schema) || '.', quote_ident(dest_schema) || '.');
|
|
1392
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
1393
|
+
qry;
|
|
1394
|
+
-- issue#95
|
|
1395
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1396
|
+
RAISE INFO 'ALTER TABLE IF EXISTS % OWNER TO %;',
|
|
1397
|
+
quote_ident(dest_schema) || '.' || quote_ident(tblname),
|
|
1398
|
+
tblowner;
|
|
1399
|
+
END IF;
|
|
1400
|
+
ELSE -- Issue#103: we need to always set search_path priority to target schema when we execute DDL
|
|
1401
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef04 context: old search path=% new search path=% current search path=%',
|
|
1402
|
+
src_path_old,
|
|
1403
|
+
src_path_new,
|
|
1404
|
+
v_dummy;
|
|
1405
|
+
END IF;
|
|
1406
|
+
SELECT setting INTO spath_tmp
|
|
1407
|
+
FROM pg_settings
|
|
1408
|
+
WHERE name = 'search_path';
|
|
1409
|
+
IF spath_tmp <> dest_schema THEN -- change it to target schema and don't forget to change it back after we execute the DDL
|
|
1410
|
+
spath = 'SET search_path = "' || dest_schema || '"';
|
|
1411
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: changing search_path --> %',
|
|
1412
|
+
spath;
|
|
1413
|
+
END IF;
|
|
1414
|
+
EXECUTE spath;
|
|
1415
|
+
SELECT setting INTO v_dummy
|
|
1416
|
+
FROM pg_settings
|
|
1417
|
+
WHERE name = 'search_path';
|
|
1418
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: search_path changed to %',
|
|
1419
|
+
v_dummy;
|
|
1420
|
+
END IF;
|
|
1421
|
+
END IF;
|
|
1422
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tabledef04:%',
|
|
1423
|
+
qry;
|
|
1424
|
+
END IF;
|
|
1425
|
+
EXECUTE qry;
|
|
1426
|
+
-- Issue#103
|
|
1427
|
+
-- Set search path back to what it was
|
|
1428
|
+
spath = 'SET search_path = "' || spath_tmp || '"';
|
|
1429
|
+
EXECUTE spath;
|
|
1430
|
+
SELECT setting INTO v_dummy
|
|
1431
|
+
FROM pg_settings
|
|
1432
|
+
WHERE name = 'search_path';
|
|
1433
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: search_path changed back to %',
|
|
1434
|
+
v_dummy;
|
|
1435
|
+
END IF;
|
|
1436
|
+
-- issue#91 fix
|
|
1437
|
+
-- issue#95
|
|
1438
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1439
|
+
buffer3 = 'ALTER TABLE IF EXISTS ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' OWNER TO ' || tblowner;
|
|
1440
|
+
EXECUTE buffer3;
|
|
1441
|
+
END IF;
|
|
1442
|
+
END IF;
|
|
1443
|
+
-- loop for child tables and alter them to attach to parent for specific partition method.
|
|
1444
|
+
-- Issue#103 fix: only loop for the table we are currently processing, tblname!
|
|
1445
|
+
FOR aname,
|
|
1446
|
+
part_range,
|
|
1447
|
+
object IN
|
|
1448
|
+
SELECT quote_ident(dest_schema) || '.' || c1.relname as tablename,
|
|
1449
|
+
pg_catalog.pg_get_expr(c1.relpartbound, c1.oid) as partrange,
|
|
1450
|
+
quote_ident(dest_schema) || '.' || c2.relname as object
|
|
1451
|
+
FROM pg_catalog.pg_class c1,
|
|
1452
|
+
pg_namespace n,
|
|
1453
|
+
pg_catalog.pg_inherits i,
|
|
1454
|
+
pg_class c2
|
|
1455
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
1456
|
+
AND c1.relnamespace = n.oid
|
|
1457
|
+
AND c1.relkind = 'r' -- Issue#103: added this condition to only work on current partitioned table. The problem was regression testing previously only worked on one partition table clone case
|
|
1458
|
+
AND c2.relname = tblname
|
|
1459
|
+
AND c1.relispartition
|
|
1460
|
+
AND c1.oid = i.inhrelid
|
|
1461
|
+
AND i.inhparent = c2.oid
|
|
1462
|
+
AND c2.relnamespace = n.oid
|
|
1463
|
+
ORDER BY pg_catalog.pg_get_expr(c1.relpartbound, c1.oid) = 'DEFAULT',
|
|
1464
|
+
c1.oid::pg_catalog.regclass::pg_catalog.text LOOP qry := 'ALTER TABLE ONLY ' || object || ' ATTACH PARTITION ' || aname || ' ' || part_range || ';';
|
|
1465
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: %',
|
|
1466
|
+
qry;
|
|
1467
|
+
END IF;
|
|
1468
|
+
-- issue#91, not sure if we need to do this for child tables
|
|
1469
|
+
-- issue#95 we dont set ownership here
|
|
1470
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
1471
|
+
qry;
|
|
1472
|
+
IF NOT bNoOwner THEN NULL;
|
|
1473
|
+
END IF;
|
|
1474
|
+
ELSE EXECUTE qry;
|
|
1475
|
+
IF NOT bNoOwner THEN NULL;
|
|
1476
|
+
END IF;
|
|
1477
|
+
END IF;
|
|
1478
|
+
END LOOP;
|
|
1479
|
+
END IF;
|
|
1480
|
+
-- INCLUDING ALL creates new index names, we restore them to the old name.
|
|
1481
|
+
-- There should be no conflicts since they live in different schemas
|
|
1482
|
+
FOR ix_old_name,
|
|
1483
|
+
ix_new_name IN
|
|
1484
|
+
SELECT old.indexname,
|
|
1485
|
+
new.indexname
|
|
1486
|
+
FROM pg_indexes old,
|
|
1487
|
+
pg_indexes new
|
|
1488
|
+
WHERE old.schemaname = source_schema
|
|
1489
|
+
AND new.schemaname = dest_schema
|
|
1490
|
+
AND old.tablename = new.tablename
|
|
1491
|
+
AND old.tablename = tblname
|
|
1492
|
+
AND old.indexname <> new.indexname
|
|
1493
|
+
AND regexp_replace(old.indexdef, E'.*USING', '') = regexp_replace(new.indexdef, E'.*USING', '')
|
|
1494
|
+
ORDER BY old.indexdef,
|
|
1495
|
+
new.indexdef LOOP lastsql = '';
|
|
1496
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
1497
|
+
'ALTER INDEX ' || quote_ident(dest_schema) || '.' || quote_ident(ix_new_name) || ' RENAME TO ' || quote_ident(ix_old_name) || ';';
|
|
1498
|
+
ELSE -- The SELECT query above may return duplicate names when a column is
|
|
1499
|
+
-- indexed twice the same manner with 2 different names. Therefore, to
|
|
1500
|
+
-- avoid a 'relation "xxx" already exists' we test if the index name
|
|
1501
|
+
-- is in use or free. Skipping existing index will fallback on unused
|
|
1502
|
+
-- ones and every duplicate will be mapped to distinct old names.
|
|
1503
|
+
IF NOT EXISTS (
|
|
1504
|
+
SELECT TRUE
|
|
1505
|
+
FROM pg_indexes
|
|
1506
|
+
WHERE schemaname = dest_schema
|
|
1507
|
+
AND tablename = tblname
|
|
1508
|
+
AND indexname = quote_ident(ix_old_name)
|
|
1509
|
+
)
|
|
1510
|
+
AND EXISTS (
|
|
1511
|
+
SELECT TRUE
|
|
1512
|
+
FROM pg_indexes
|
|
1513
|
+
WHERE schemaname = dest_schema
|
|
1514
|
+
AND tablename = tblname
|
|
1515
|
+
AND indexname = quote_ident(ix_new_name)
|
|
1516
|
+
) THEN EXECUTE 'ALTER INDEX ' || quote_ident(dest_schema) || '.' || quote_ident(ix_new_name) || ' RENAME TO ' || quote_ident(ix_old_name) || ';';
|
|
1517
|
+
END IF;
|
|
1518
|
+
END IF;
|
|
1519
|
+
END LOOP;
|
|
1520
|
+
IF bData THEN -- Insert records from source table
|
|
1521
|
+
-- 2021-03-03 MJV FIX
|
|
1522
|
+
buffer := dest_schema || '.' || quote_ident(tblname);
|
|
1523
|
+
-- Issue#86 fix:
|
|
1524
|
+
-- IF data_type = 'USER-DEFINED' THEN
|
|
1525
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: includerecs branch table=% data_type=% isgenerated=%',
|
|
1526
|
+
tblname,
|
|
1527
|
+
data_type,
|
|
1528
|
+
isGenerated;
|
|
1529
|
+
END IF;
|
|
1530
|
+
IF data_type = 'USER-DEFINED'
|
|
1531
|
+
OR isGenerated = 'ALWAYS' THEN -- RAISE WARNING 'Bypassing copying rows for table (%) with user-defined data types. You must copy them manually.', tblname;
|
|
1532
|
+
-- wont work --> INSERT INTO clone1.address (id2, id3, addr) SELECT cast(id2 as clone1.udt_myint), cast(id3 as clone1.udt_myint), addr FROM sample.address;
|
|
1533
|
+
-- Issue#101 --> INSERT INTO clone1.address2 (id2, id3, addr) SELECT id2::text::clone1.udt_myint, id3::text::clone1.udt_myint, addr FROM sample.address;
|
|
1534
|
+
-- Issue#79 implementation follows
|
|
1535
|
+
-- COPY sample.statuses(id, s) TO '/tmp/statuses.txt' WITH DELIMITER AS ',';
|
|
1536
|
+
-- COPY sample_clone1.statuses FROM '/tmp/statuses.txt' (DELIMITER ',', NULL '');
|
|
1537
|
+
-- Issue#101 fix: use text cast to get around the problem.
|
|
1538
|
+
IF bFileCopy THEN IF bWindows THEN buffer2 := 'COPY ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ' TO ''C:\WINDOWS\TEMP\cloneschema.tmp'' WITH DELIMITER AS '','';';
|
|
1539
|
+
tblarray2 := tblarray2 || buffer2;
|
|
1540
|
+
-- Issue #81 reformat COPY command for upload
|
|
1541
|
+
-- buffer2:= 'COPY ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' FROM ''C:\WINDOWS\TEMP\cloneschema.tmp'' (DELIMITER '','', NULL '''');';
|
|
1542
|
+
buffer2 := 'COPY ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' FROM ''C:\WINDOWS\TEMP\cloneschema.tmp'' (DELIMITER '','', NULL ''\N'', FORMAT CSV);';
|
|
1543
|
+
tblarray2 := tblarray2 || buffer2;
|
|
1544
|
+
ELSE buffer2 := 'COPY ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ' TO ''/tmp/cloneschema.tmp'' WITH DELIMITER AS '','';';
|
|
1545
|
+
tblarray2 := tblarray2 || buffer2;
|
|
1546
|
+
-- Issue #81 reformat COPY command for upload
|
|
1547
|
+
-- buffer2 := 'COPY ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' FROM ''/tmp/cloneschema.tmp'' (DELIMITER '','', NULL '''');';
|
|
1548
|
+
-- works--> COPY sample.timestamptbl2 FROM '/tmp/cloneschema.tmp' WITH (DELIMITER ',', NULL '\N', FORMAT CSV) ;
|
|
1549
|
+
buffer2 := 'COPY ' || quote_ident(dest_schema) || '.' || quote_ident(tblname) || ' FROM ''/tmp/cloneschema.tmp'' (DELIMITER '','', NULL ''\N'', FORMAT CSV);';
|
|
1550
|
+
tblarray2 := tblarray2 || buffer2;
|
|
1551
|
+
END IF;
|
|
1552
|
+
ELSE -- Issue#101: assume direct copy with text cast, add to separate array
|
|
1553
|
+
SELECT * INTO buffer3
|
|
1554
|
+
FROM public.get_insert_stmt_ddl(
|
|
1555
|
+
quote_ident(source_schema),
|
|
1556
|
+
quote_ident(dest_schema),
|
|
1557
|
+
quote_ident(tblname),
|
|
1558
|
+
True
|
|
1559
|
+
);
|
|
1560
|
+
tblarray3 := tblarray3 || buffer3;
|
|
1561
|
+
END IF;
|
|
1562
|
+
ELSE -- bypass child tables since we populate them when we populate the parents
|
|
1563
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: tblname=% bRelispart=NA relknd=% l_child=% bChild=%',
|
|
1564
|
+
tblname,
|
|
1565
|
+
relknd,
|
|
1566
|
+
l_child,
|
|
1567
|
+
bChild;
|
|
1568
|
+
END IF;
|
|
1569
|
+
IF NOT bChild THEN -- Issue#75: Must defer population of tables until child tables have been added to parents
|
|
1570
|
+
-- Issue#101 Offer alternative of copy to/from file. Although originally intended for tables with UDTs, it is now expanded to handle all cases for performance improvement perhaps for large tables.
|
|
1571
|
+
-- buffer2 := 'INSERT INTO ' || buffer || buffer3 || ' SELECT * FROM ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ';';
|
|
1572
|
+
buffer2 := 'INSERT INTO ' || buffer || ' SELECT * FROM ' || quote_ident(source_schema) || '.' || quote_ident(tblname) || ';';
|
|
1573
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: buffer2=%',
|
|
1574
|
+
buffer2;
|
|
1575
|
+
END IF;
|
|
1576
|
+
IF bFileCopy THEN tblarray2 := tblarray2 || buffer2;
|
|
1577
|
+
ELSE tblarray := tblarray || buffer2;
|
|
1578
|
+
END IF;
|
|
1579
|
+
END IF;
|
|
1580
|
+
END IF;
|
|
1581
|
+
END IF;
|
|
1582
|
+
-- Issue#61 FIX: use set_config for empty string
|
|
1583
|
+
-- SET search_path = '';
|
|
1584
|
+
SELECT set_config('search_path', '', false) into v_dummy;
|
|
1585
|
+
FOR column_,
|
|
1586
|
+
default_ IN
|
|
1587
|
+
SELECT column_name::text,
|
|
1588
|
+
REPLACE(
|
|
1589
|
+
column_default::text,
|
|
1590
|
+
quote_ident(source_schema) || '.',
|
|
1591
|
+
quote_ident(dest_schema) || '.'
|
|
1592
|
+
)
|
|
1593
|
+
FROM information_schema.COLUMNS
|
|
1594
|
+
WHERE table_schema = source_schema
|
|
1595
|
+
AND TABLE_NAME = tblname
|
|
1596
|
+
AND column_default LIKE 'nextval(%' || quote_ident(source_schema) || '%::regclass)' LOOP -- Issue#78 FIX: handle case-sensitive names with quote_ident() on column name
|
|
1597
|
+
buffer2 = 'ALTER TABLE ' || buffer || ' ALTER COLUMN ' || quote_ident(column_) || ' SET DEFAULT ' || default_ || ';';
|
|
1598
|
+
IF bDDLOnly THEN -- May need to come back and revisit this since previous sql will not return anything since no schema as created!
|
|
1599
|
+
RAISE INFO '%',
|
|
1600
|
+
buffer2;
|
|
1601
|
+
ELSE EXECUTE buffer2;
|
|
1602
|
+
END IF;
|
|
1603
|
+
END LOOP;
|
|
1604
|
+
EXECUTE 'SET search_path = ' || quote_ident(source_schema);
|
|
1605
|
+
END LOOP;
|
|
1606
|
+
END IF;
|
|
1607
|
+
-- end of 90600 branch
|
|
1608
|
+
RAISE NOTICE ' TABLES cloned: %',
|
|
1609
|
+
LPAD(cnt::text, 5, ' ');
|
|
1610
|
+
SELECT setting INTO v_dummy
|
|
1611
|
+
FROM pg_settings
|
|
1612
|
+
WHERE name = 'search_path';
|
|
1613
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: search_path=%',
|
|
1614
|
+
v_dummy;
|
|
1615
|
+
END IF;
|
|
1616
|
+
-- Assigning sequences to table columns.
|
|
1617
|
+
action := 'Sequences assigning';
|
|
1618
|
+
cnt := 0;
|
|
1619
|
+
FOR object IN
|
|
1620
|
+
SELECT sequence_name::text
|
|
1621
|
+
FROM information_schema.sequences
|
|
1622
|
+
WHERE sequence_schema = quote_ident(source_schema) LOOP cnt := cnt + 1;
|
|
1623
|
+
srctbl := quote_ident(source_schema) || '.' || quote_ident(object);
|
|
1624
|
+
-- Get owning column, inspired from Sadique Ali post at:
|
|
1625
|
+
-- https://sadique.io/blog/2019/05/07/viewing-sequence-ownership-information-in-postgres/
|
|
1626
|
+
-- Fixed via pull request#109
|
|
1627
|
+
SELECT ' OWNED BY ' || quote_ident(dest_schema) || '.' || quote_ident(dc.relname) || '.' || quote_ident(a.attname) INTO sq_owned
|
|
1628
|
+
FROM pg_class AS c
|
|
1629
|
+
JOIN pg_namespace n ON c.relnamespace = n.oid
|
|
1630
|
+
JOIN pg_depend AS d ON c.relfilenode = d.objid
|
|
1631
|
+
JOIN pg_class AS dc ON (
|
|
1632
|
+
d.refobjid = dc.relfilenode
|
|
1633
|
+
AND dc.relnamespace = n.oid
|
|
1634
|
+
)
|
|
1635
|
+
JOIN pg_attribute AS a ON (
|
|
1636
|
+
a.attnum = d.refobjsubid
|
|
1637
|
+
AND a.attrelid = d.refobjid
|
|
1638
|
+
)
|
|
1639
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
1640
|
+
AND c.relkind = 'S'
|
|
1641
|
+
AND c.relname = object;
|
|
1642
|
+
IF sq_owned IS NOT NULL THEN qry := 'ALTER SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object) || sq_owned || ';';
|
|
1643
|
+
IF bDDLOnly THEN RAISE NOTICE 'DEBUG: %',
|
|
1644
|
+
qry;
|
|
1645
|
+
RAISE INFO '%',
|
|
1646
|
+
qry;
|
|
1647
|
+
ELSE EXECUTE qry;
|
|
1648
|
+
END IF;
|
|
1649
|
+
END IF;
|
|
1650
|
+
END LOOP;
|
|
1651
|
+
RAISE NOTICE ' SEQUENCES set: %',
|
|
1652
|
+
LPAD(cnt::text, 5, ' ');
|
|
1653
|
+
-- Update IDENTITY sequences to the last value, bypass 9.6 versions
|
|
1654
|
+
IF sq_server_version_num > 90624 THEN action := 'Identity updating';
|
|
1655
|
+
cnt := 0;
|
|
1656
|
+
FOR object,
|
|
1657
|
+
sq_last_value IN
|
|
1658
|
+
SELECT sequencename::text,
|
|
1659
|
+
COALESCE(last_value, -999)
|
|
1660
|
+
from pg_sequences
|
|
1661
|
+
where schemaname = quote_ident(source_schema)
|
|
1662
|
+
AND NOT EXISTS (
|
|
1663
|
+
select 1
|
|
1664
|
+
from information_schema.sequences
|
|
1665
|
+
where sequence_schema = quote_ident(source_schema)
|
|
1666
|
+
and sequence_name = sequencename
|
|
1667
|
+
) LOOP IF sq_last_value = -999 THEN continue;
|
|
1668
|
+
END IF;
|
|
1669
|
+
cnt := cnt + 1;
|
|
1670
|
+
buffer := quote_ident(dest_schema) || '.' || quote_ident(object);
|
|
1671
|
+
IF bData THEN EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_last_value || ', ' || sq_is_called || ');';
|
|
1672
|
+
ELSE if bDDLOnly THEN -- fix#63
|
|
1673
|
+
RAISE INFO '%',
|
|
1674
|
+
'SELECT setval( ''' || buffer || ''', ' || sq_last_value || ', ' || sq_is_called || ');';
|
|
1675
|
+
ELSE -- fix#63
|
|
1676
|
+
EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_last_value || ', ' || sq_is_called || ');';
|
|
1677
|
+
END IF;
|
|
1678
|
+
END IF;
|
|
1679
|
+
END LOOP;
|
|
1680
|
+
-- Fixed Issue#107: set lpad from 2 to 5
|
|
1681
|
+
RAISE NOTICE ' IDENTITIES set: %',
|
|
1682
|
+
LPAD(cnt::text, 5, ' ');
|
|
1683
|
+
ELSE -- Fixed Issue#107: set lpad from 2 to 5
|
|
1684
|
+
RAISE NOTICE ' IDENTITIES set: %',
|
|
1685
|
+
LPAD('-1'::text, 5, ' ');
|
|
1686
|
+
END IF;
|
|
1687
|
+
-- Issue#78 forces us to defer FKeys until the end since we previously did row copies before FKeys
|
|
1688
|
+
-- add FK constraint
|
|
1689
|
+
-- action := 'FK Constraints';
|
|
1690
|
+
-- Issue#62: Add comments on indexes, and then removed them from here and reworked later below.
|
|
1691
|
+
-- Issue 90: moved functions to here, before views or MVs that might use them
|
|
1692
|
+
-- Create functions
|
|
1693
|
+
action := 'Functions';
|
|
1694
|
+
cnt := 0;
|
|
1695
|
+
-- MJV FIX per issue# 34
|
|
1696
|
+
-- SET search_path = '';
|
|
1697
|
+
EXECUTE 'SET search_path = ' || quote_ident(source_schema);
|
|
1698
|
+
-- Fixed Issue#65
|
|
1699
|
+
-- Fixed Issue#97
|
|
1700
|
+
-- FOR func_oid IN SELECT oid FROM pg_proc WHERE pronamespace = src_oid AND prokind != 'a'
|
|
1701
|
+
IF is_prokind THEN FOR func_oid,
|
|
1702
|
+
func_owner,
|
|
1703
|
+
func_name,
|
|
1704
|
+
func_args,
|
|
1705
|
+
func_argno,
|
|
1706
|
+
buffer3 IN
|
|
1707
|
+
SELECT p.oid,
|
|
1708
|
+
pg_catalog.pg_get_userbyid(p.proowner),
|
|
1709
|
+
p.proname,
|
|
1710
|
+
oidvectortypes(p.proargtypes),
|
|
1711
|
+
p.pronargs,
|
|
1712
|
+
CASE
|
|
1713
|
+
WHEN prokind = 'p' THEN 'PROCEDURE'
|
|
1714
|
+
WHEN prokind = 'f' THEN 'FUNCTION'
|
|
1715
|
+
ELSE ''
|
|
1716
|
+
END
|
|
1717
|
+
FROM pg_proc p
|
|
1718
|
+
WHERE p.pronamespace = src_oid
|
|
1719
|
+
AND p.prokind != 'a' LOOP cnt := cnt + 1;
|
|
1720
|
+
SELECT pg_get_functiondef(func_oid) INTO qry;
|
|
1721
|
+
SELECT replace(
|
|
1722
|
+
qry,
|
|
1723
|
+
quote_ident(source_schema) || '.',
|
|
1724
|
+
quote_ident(dest_schema) || '.'
|
|
1725
|
+
) INTO dest_qry;
|
|
1726
|
+
IF bDDLOnly THEN RAISE INFO '%;',
|
|
1727
|
+
dest_qry;
|
|
1728
|
+
-- Issue#91 Fix
|
|
1729
|
+
-- issue#95
|
|
1730
|
+
IF NOT bNoOwner THEN IF func_argno = 0 THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1731
|
+
RAISE INFO 'ALTER % %() OWNER TO %',
|
|
1732
|
+
buffer3,
|
|
1733
|
+
quote_ident(dest_schema) || '.' || quote_ident(func_name),
|
|
1734
|
+
'"' || func_owner || '";';
|
|
1735
|
+
ELSE -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1736
|
+
RAISE INFO 'ALTER % % OWNER TO %',
|
|
1737
|
+
buffer3,
|
|
1738
|
+
quote_ident(dest_schema) || '.' || quote_ident(func_name) || '(' || func_args || ')',
|
|
1739
|
+
'"' || func_owner || '";';
|
|
1740
|
+
END IF;
|
|
1741
|
+
END IF;
|
|
1742
|
+
ELSE IF bDebug THEN RAISE NOTICE 'DEBUG: %',
|
|
1743
|
+
dest_qry;
|
|
1744
|
+
END IF;
|
|
1745
|
+
EXECUTE dest_qry;
|
|
1746
|
+
-- Issue#91 Fix
|
|
1747
|
+
-- issue#95
|
|
1748
|
+
IF NOT bNoOwner THEN IF func_argno = 0 THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1749
|
+
dest_qry = 'ALTER ' || buffer3 || ' ' || quote_ident(dest_schema) || '.' || quote_ident(func_name) || '() OWNER TO ' || '"' || func_owner || '";';
|
|
1750
|
+
ELSE -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1751
|
+
dest_qry = 'ALTER ' || buffer3 || ' ' || quote_ident(dest_schema) || '.' || quote_ident(func_name) || '(' || func_args || ') OWNER TO ' || '"' || func_owner || '";';
|
|
1752
|
+
END IF;
|
|
1753
|
+
END IF;
|
|
1754
|
+
EXECUTE dest_qry;
|
|
1755
|
+
END IF;
|
|
1756
|
+
END LOOP;
|
|
1757
|
+
ELSE FOR func_oid IN
|
|
1758
|
+
SELECT oid
|
|
1759
|
+
FROM pg_proc
|
|
1760
|
+
WHERE pronamespace = src_oid
|
|
1761
|
+
AND not proisagg LOOP cnt := cnt + 1;
|
|
1762
|
+
SELECT pg_get_functiondef(func_oid) INTO qry;
|
|
1763
|
+
SELECT replace(
|
|
1764
|
+
qry,
|
|
1765
|
+
quote_ident(source_schema) || '.',
|
|
1766
|
+
quote_ident(dest_schema) || '.'
|
|
1767
|
+
) INTO dest_qry;
|
|
1768
|
+
IF bDDLOnly THEN RAISE INFO '%;',
|
|
1769
|
+
dest_qry;
|
|
1770
|
+
ELSE EXECUTE dest_qry;
|
|
1771
|
+
END IF;
|
|
1772
|
+
END LOOP;
|
|
1773
|
+
END IF;
|
|
1774
|
+
-- Create aggregate functions.
|
|
1775
|
+
-- Fixed Issue#65
|
|
1776
|
+
-- FOR func_oid IN SELECT oid FROM pg_proc WHERE pronamespace = src_oid AND prokind = 'a'
|
|
1777
|
+
IF is_prokind THEN FOR func_oid IN
|
|
1778
|
+
SELECT oid
|
|
1779
|
+
FROM pg_proc
|
|
1780
|
+
WHERE pronamespace = src_oid
|
|
1781
|
+
AND prokind = 'a' LOOP cnt := cnt + 1;
|
|
1782
|
+
SELECT 'CREATE AGGREGATE ' || dest_schema || '.' || p.proname || '(' -- || format_type(a.aggtranstype, NULL)
|
|
1783
|
+
-- Issue#65 Fixes for specific datatype mappings
|
|
1784
|
+
|| CASE
|
|
1785
|
+
WHEN format_type(a.aggtranstype, NULL) = 'double precision[]' THEN 'float8'
|
|
1786
|
+
WHEN format_type(a.aggtranstype, NULL) = 'anyarray' THEN 'anyelement'
|
|
1787
|
+
ELSE format_type(a.aggtranstype, NULL)
|
|
1788
|
+
END || ') (sfunc = ' || regexp_replace(
|
|
1789
|
+
a.aggtransfn::text,
|
|
1790
|
+
'(^|\W)' || quote_ident(source_schema) || '\.',
|
|
1791
|
+
'\1' || quote_ident(dest_schema) || '.'
|
|
1792
|
+
) || ', stype = ' -- || format_type(a.aggtranstype, NULL)
|
|
1793
|
+
-- Issue#65 Fixes for specific datatype mappings
|
|
1794
|
+
|| CASE
|
|
1795
|
+
WHEN format_type(a.aggtranstype, NULL) = 'double precision[]' THEN 'float8[]'
|
|
1796
|
+
ELSE format_type(a.aggtranstype, NULL)
|
|
1797
|
+
END || CASE
|
|
1798
|
+
WHEN op.oprname IS NULL THEN ''
|
|
1799
|
+
ELSE ', sortop = ' || op.oprname
|
|
1800
|
+
END || CASE
|
|
1801
|
+
WHEN a.agginitval IS NULL THEN ''
|
|
1802
|
+
ELSE ', initcond = ''' || a.agginitval || ''''
|
|
1803
|
+
END || ')' INTO dest_qry
|
|
1804
|
+
FROM pg_proc p
|
|
1805
|
+
JOIN pg_aggregate a ON a.aggfnoid = p.oid
|
|
1806
|
+
LEFT JOIN pg_operator op ON op.oid = a.aggsortop
|
|
1807
|
+
WHERE p.oid = func_oid;
|
|
1808
|
+
IF bDDLOnly THEN RAISE INFO '%;',
|
|
1809
|
+
dest_qry;
|
|
1810
|
+
ELSE EXECUTE dest_qry;
|
|
1811
|
+
END IF;
|
|
1812
|
+
END LOOP;
|
|
1813
|
+
RAISE NOTICE ' FUNCTIONS cloned: %',
|
|
1814
|
+
LPAD(cnt::text, 5, ' ');
|
|
1815
|
+
ELSE FOR func_oid IN
|
|
1816
|
+
SELECT oid
|
|
1817
|
+
FROM pg_proc
|
|
1818
|
+
WHERE pronamespace = src_oid
|
|
1819
|
+
AND proisagg LOOP cnt := cnt + 1;
|
|
1820
|
+
SELECT 'CREATE AGGREGATE ' || dest_schema || '.' || p.proname || '(' -- || format_type(a.aggtranstype, NULL)
|
|
1821
|
+
-- Issue#65 Fixes for specific datatype mappings
|
|
1822
|
+
|| CASE
|
|
1823
|
+
WHEN format_type(a.aggtranstype, NULL) = 'double precision[]' THEN 'float8'
|
|
1824
|
+
WHEN format_type(a.aggtranstype, NULL) = 'anyarray' THEN 'anyelement'
|
|
1825
|
+
ELSE format_type(a.aggtranstype, NULL)
|
|
1826
|
+
END || ') (sfunc = ' || regexp_replace(
|
|
1827
|
+
a.aggtransfn::text,
|
|
1828
|
+
'(^|\W)' || quote_ident(source_schema) || '\.',
|
|
1829
|
+
'\1' || quote_ident(dest_schema) || '.'
|
|
1830
|
+
) || ', stype = ' -- || format_type(a.aggtranstype, NULL)
|
|
1831
|
+
-- Issue#65 Fixes for specific datatype mappings
|
|
1832
|
+
|| CASE
|
|
1833
|
+
WHEN format_type(a.aggtranstype, NULL) = 'double precision[]' THEN 'float8[]'
|
|
1834
|
+
ELSE format_type(a.aggtranstype, NULL)
|
|
1835
|
+
END || CASE
|
|
1836
|
+
WHEN op.oprname IS NULL THEN ''
|
|
1837
|
+
ELSE ', sortop = ' || op.oprname
|
|
1838
|
+
END || CASE
|
|
1839
|
+
WHEN a.agginitval IS NULL THEN ''
|
|
1840
|
+
ELSE ', initcond = ''' || a.agginitval || ''''
|
|
1841
|
+
END || ')' INTO dest_qry
|
|
1842
|
+
FROM pg_proc p
|
|
1843
|
+
JOIN pg_aggregate a ON a.aggfnoid = p.oid
|
|
1844
|
+
LEFT JOIN pg_operator op ON op.oid = a.aggsortop
|
|
1845
|
+
WHERE p.oid = func_oid;
|
|
1846
|
+
IF bDDLOnly THEN RAISE INFO '%;',
|
|
1847
|
+
dest_qry;
|
|
1848
|
+
ELSE EXECUTE dest_qry;
|
|
1849
|
+
END IF;
|
|
1850
|
+
END LOOP;
|
|
1851
|
+
RAISE NOTICE ' FUNCTIONS cloned: %',
|
|
1852
|
+
LPAD(cnt::text, 5, ' ');
|
|
1853
|
+
END IF;
|
|
1854
|
+
-- Create views
|
|
1855
|
+
action := 'Views';
|
|
1856
|
+
-- Issue#61 FIX: use set_config for empty string
|
|
1857
|
+
-- MJV FIX #43: also had to reset search_path from source schema to empty.
|
|
1858
|
+
-- SET search_path = '';
|
|
1859
|
+
SELECT set_config('search_path', '', false) INTO v_dummy;
|
|
1860
|
+
cnt := 0;
|
|
1861
|
+
--FOR object IN
|
|
1862
|
+
-- SELECT table_name::text, view_definition
|
|
1863
|
+
-- FROM information_schema.views
|
|
1864
|
+
-- WHERE table_schema = quote_ident(source_schema)
|
|
1865
|
+
-- Issue#73 replace loop query to handle dependencies
|
|
1866
|
+
-- Issue#91 get view_owner
|
|
1867
|
+
FOR srctbl,
|
|
1868
|
+
aname,
|
|
1869
|
+
view_owner,
|
|
1870
|
+
object IN WITH RECURSIVE views AS (
|
|
1871
|
+
SELECT n.nspname as schemaname,
|
|
1872
|
+
v.relname as tablename,
|
|
1873
|
+
v.oid::regclass AS viewname,
|
|
1874
|
+
v.relkind = 'm' AS is_materialized,
|
|
1875
|
+
pg_catalog.pg_get_userbyid(v.relowner) as owner,
|
|
1876
|
+
1 AS level
|
|
1877
|
+
FROM pg_depend AS d
|
|
1878
|
+
JOIN pg_rewrite AS r ON r.oid = d.objid
|
|
1879
|
+
JOIN pg_class AS v ON v.oid = r.ev_class
|
|
1880
|
+
JOIN pg_namespace n ON n.oid = v.relnamespace -- WHERE v.relkind IN ('v', 'm')
|
|
1881
|
+
WHERE v.relkind IN ('v')
|
|
1882
|
+
AND d.classid = 'pg_rewrite'::regclass
|
|
1883
|
+
AND d.refclassid = 'pg_class'::regclass
|
|
1884
|
+
AND d.deptype = 'n'
|
|
1885
|
+
UNION
|
|
1886
|
+
-- add the views that depend on these
|
|
1887
|
+
SELECT n.nspname as schemaname,
|
|
1888
|
+
v.relname as tablename,
|
|
1889
|
+
v.oid::regclass AS viewname,
|
|
1890
|
+
v.relkind = 'm',
|
|
1891
|
+
pg_catalog.pg_get_userbyid(v.relowner) as owner,
|
|
1892
|
+
views.level + 1
|
|
1893
|
+
FROM views
|
|
1894
|
+
JOIN pg_depend AS d ON d.refobjid = views.viewname
|
|
1895
|
+
JOIN pg_rewrite AS r ON r.oid = d.objid
|
|
1896
|
+
JOIN pg_class AS v ON v.oid = r.ev_class
|
|
1897
|
+
JOIN pg_namespace n ON n.oid = v.relnamespace -- WHERE v.relkind IN ('v', 'm')
|
|
1898
|
+
WHERE v.relkind IN ('v')
|
|
1899
|
+
AND d.classid = 'pg_rewrite'::regclass
|
|
1900
|
+
AND d.refclassid = 'pg_class'::regclass
|
|
1901
|
+
AND d.deptype = 'n'
|
|
1902
|
+
AND v.oid <> views.viewname
|
|
1903
|
+
)
|
|
1904
|
+
SELECT tablename,
|
|
1905
|
+
viewname,
|
|
1906
|
+
owner,
|
|
1907
|
+
format(
|
|
1908
|
+
'CREATE OR REPLACE%s VIEW %s AS%s',
|
|
1909
|
+
CASE
|
|
1910
|
+
WHEN is_materialized THEN ' MATERIALIZED'
|
|
1911
|
+
ELSE ''
|
|
1912
|
+
END,
|
|
1913
|
+
viewname,
|
|
1914
|
+
pg_get_viewdef(viewname)
|
|
1915
|
+
)
|
|
1916
|
+
FROM views
|
|
1917
|
+
WHERE schemaname = quote_ident(source_schema)
|
|
1918
|
+
GROUP BY schemaname,
|
|
1919
|
+
tablename,
|
|
1920
|
+
viewname,
|
|
1921
|
+
owner,
|
|
1922
|
+
is_materialized
|
|
1923
|
+
ORDER BY max(level),
|
|
1924
|
+
schemaname,
|
|
1925
|
+
tablename LOOP cnt := cnt + 1;
|
|
1926
|
+
-- Issue#73 replace logic based on new loop sql
|
|
1927
|
+
buffer := quote_ident(dest_schema) || '.' || quote_ident(aname);
|
|
1928
|
+
-- MJV FIX: #43
|
|
1929
|
+
-- SELECT view_definition INTO v_def
|
|
1930
|
+
-- SELECT REPLACE(view_definition, quote_ident(source_schema) || '.', quote_ident(dest_schema) || '.') INTO v_def
|
|
1931
|
+
-- FROM information_schema.views
|
|
1932
|
+
-- WHERE table_schema = quote_ident(source_schema)
|
|
1933
|
+
-- AND table_name = quote_ident(object);
|
|
1934
|
+
SELECT REPLACE(
|
|
1935
|
+
object,
|
|
1936
|
+
quote_ident(source_schema) || '.',
|
|
1937
|
+
quote_ident(dest_schema) || '.'
|
|
1938
|
+
) INTO v_def;
|
|
1939
|
+
-- NOTE: definition already includes the closing statement semicolon
|
|
1940
|
+
SELECT REPLACE(
|
|
1941
|
+
aname,
|
|
1942
|
+
quote_ident(source_schema) || '.',
|
|
1943
|
+
quote_ident(dest_schema) || '.'
|
|
1944
|
+
) INTO buffer3;
|
|
1945
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
1946
|
+
v_def;
|
|
1947
|
+
-- Issue#91 Fix
|
|
1948
|
+
-- issue#95
|
|
1949
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1950
|
+
-- RAISE INFO 'ALTER TABLE % OWNER TO %', buffer3, view_owner || ';';
|
|
1951
|
+
RAISE INFO 'ALTER TABLE % OWNER TO %',
|
|
1952
|
+
buffer3,
|
|
1953
|
+
'"' || view_owner || '";';
|
|
1954
|
+
END IF;
|
|
1955
|
+
ELSE -- EXECUTE 'CREATE OR REPLACE VIEW ' || buffer || ' AS ' || v_def;
|
|
1956
|
+
EXECUTE v_def;
|
|
1957
|
+
-- Issue#73: commented out comment logic for views since we do it elsewhere now.
|
|
1958
|
+
-- Issue#91 Fix
|
|
1959
|
+
-- issue#95
|
|
1960
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1961
|
+
v_def = 'ALTER TABLE ' || buffer3 || ' OWNER TO ' || '"' || view_owner || '";';
|
|
1962
|
+
EXECUTE v_def;
|
|
1963
|
+
END IF;
|
|
1964
|
+
END IF;
|
|
1965
|
+
END LOOP;
|
|
1966
|
+
RAISE NOTICE ' VIEWS cloned: %',
|
|
1967
|
+
LPAD(cnt::text, 5, ' ');
|
|
1968
|
+
-- Create Materialized views
|
|
1969
|
+
action := 'Mat. Views';
|
|
1970
|
+
cnt := 0;
|
|
1971
|
+
-- Issue#91 get view_owner
|
|
1972
|
+
FOR object,
|
|
1973
|
+
view_owner,
|
|
1974
|
+
v_def IN
|
|
1975
|
+
SELECT matviewname::text,
|
|
1976
|
+
'"' || matviewowner::text || '"',
|
|
1977
|
+
replace(definition, ';', '')
|
|
1978
|
+
FROM pg_catalog.pg_matviews
|
|
1979
|
+
WHERE schemaname = quote_ident(source_schema) LOOP cnt := cnt + 1;
|
|
1980
|
+
-- Issue#78 FIX: handle case-sensitive names with quote_ident() on target schema and object
|
|
1981
|
+
buffer := quote_ident(dest_schema) || '.' || quote_ident(object);
|
|
1982
|
+
-- MJV FIX: #72 remove source schema in MV def
|
|
1983
|
+
SELECT REPLACE(
|
|
1984
|
+
v_def,
|
|
1985
|
+
quote_ident(source_schema) || '.',
|
|
1986
|
+
quote_ident(dest_schema) || '.'
|
|
1987
|
+
) INTO buffer2;
|
|
1988
|
+
IF bData THEN -- issue#98 defer creation until after regular tables are populated. Also defer the ownership as well.
|
|
1989
|
+
-- EXECUTE 'CREATE MATERIALIZED VIEW ' || buffer || ' AS ' || buffer2 || ' WITH DATA;' ;
|
|
1990
|
+
buffer3 = 'CREATE MATERIALIZED VIEW ' || buffer || ' AS ' || buffer2 || ' WITH DATA;';
|
|
1991
|
+
mvarray := mvarray || buffer3;
|
|
1992
|
+
-- issue#95
|
|
1993
|
+
IF NOT bNoOwner THEN -- buffer3 = 'ALTER MATERIALIZED VIEW ' || buffer || ' OWNER TO ' || view_owner || ';' ;
|
|
1994
|
+
-- EXECUTE buffer3;
|
|
1995
|
+
-- Fixed Issue#108: double-quote roles in case they have special characters
|
|
1996
|
+
buffer3 = 'ALTER MATERIALIZED VIEW ' || buffer || ' OWNER TO ' || view_owner || ';';
|
|
1997
|
+
mvarray := mvarray || buffer3;
|
|
1998
|
+
END IF;
|
|
1999
|
+
ELSE IF bDDLOnly THEN RAISE INFO '%',
|
|
2000
|
+
'CREATE MATERIALIZED VIEW ' || buffer || ' AS ' || buffer2 || ' WITH NO DATA;';
|
|
2001
|
+
-- Issue#91
|
|
2002
|
+
-- issue#95
|
|
2003
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
2004
|
+
RAISE INFO '%',
|
|
2005
|
+
'ALTER MATERIALIZED VIEW ' || buffer || ' OWNER TO ' || view_owner || ';';
|
|
2006
|
+
END IF;
|
|
2007
|
+
ELSE EXECUTE 'CREATE MATERIALIZED VIEW ' || buffer || ' AS ' || buffer2 || ' WITH NO DATA;';
|
|
2008
|
+
-- Issue#91
|
|
2009
|
+
-- issue#95
|
|
2010
|
+
IF NOT bNoOwner THEN -- Fixed Issue#108: double-quote roles in case they have special characters
|
|
2011
|
+
buffer3 = 'ALTER MATERIALIZED VIEW ' || buffer || ' OWNER TO ' || view_owner || ';';
|
|
2012
|
+
EXECUTE buffer3;
|
|
2013
|
+
END IF;
|
|
2014
|
+
END IF;
|
|
2015
|
+
END IF;
|
|
2016
|
+
SELECT coalesce(obj_description(oid), '') into adef
|
|
2017
|
+
from pg_class
|
|
2018
|
+
where relkind = 'm'
|
|
2019
|
+
and relname = object;
|
|
2020
|
+
IF adef <> '' THEN IF bDDLOnly THEN RAISE INFO '%',
|
|
2021
|
+
'COMMENT ON MATERIALIZED VIEW ' || quote_ident(dest_schema) || '.' || object || ' IS ''' || adef || ''';';
|
|
2022
|
+
ELSE -- Issue#$98: also defer if copy rows is on since we defer MVIEWS in that case
|
|
2023
|
+
IF bData THEN buffer3 = 'COMMENT ON MATERIALIZED VIEW ' || quote_ident(dest_schema) || '.' || object || ' IS ''' || adef || ''';';
|
|
2024
|
+
mvarray = mvarray || buffer3;
|
|
2025
|
+
ELSE EXECUTE 'COMMENT ON MATERIALIZED VIEW ' || quote_ident(dest_schema) || '.' || object || ' IS ''' || adef || ''';';
|
|
2026
|
+
END IF;
|
|
2027
|
+
END IF;
|
|
2028
|
+
END IF;
|
|
2029
|
+
FOR aname,
|
|
2030
|
+
adef IN
|
|
2031
|
+
SELECT indexname,
|
|
2032
|
+
replace(
|
|
2033
|
+
indexdef,
|
|
2034
|
+
quote_ident(source_schema) || '.',
|
|
2035
|
+
quote_ident(dest_schema) || '.'
|
|
2036
|
+
) as newdef
|
|
2037
|
+
FROM pg_indexes
|
|
2038
|
+
where schemaname = quote_ident(source_schema)
|
|
2039
|
+
and tablename = object
|
|
2040
|
+
order by indexname LOOP IF bDDLOnly THEN RAISE INFO '%',
|
|
2041
|
+
adef || ';';
|
|
2042
|
+
ELSE EXECUTE adef || ';';
|
|
2043
|
+
END IF;
|
|
2044
|
+
END LOOP;
|
|
2045
|
+
END LOOP;
|
|
2046
|
+
RAISE NOTICE ' MAT VIEWS cloned: %',
|
|
2047
|
+
LPAD(cnt::text, 5, ' ');
|
|
2048
|
+
-- Issue 90 Move create functions to before views
|
|
2049
|
+
-- MV: Create Triggers
|
|
2050
|
+
-- MJV FIX: #38
|
|
2051
|
+
-- EXECUTE 'SET search_path = ' || quote_ident(source_schema) ;
|
|
2052
|
+
-- Issue#61 FIX: use set_config for empty string
|
|
2053
|
+
-- SET search_path = '';
|
|
2054
|
+
SELECT set_config('search_path', '', false) into v_dummy;
|
|
2055
|
+
action := 'Triggers';
|
|
2056
|
+
cnt := 0;
|
|
2057
|
+
FOR arec IN -- 2021-03-09 MJV FIX: #40 fixed sql to get the def using pg_get_triggerdef() sql
|
|
2058
|
+
SELECT n.nspname,
|
|
2059
|
+
c.relname,
|
|
2060
|
+
t.tgname,
|
|
2061
|
+
p.proname,
|
|
2062
|
+
REPLACE(
|
|
2063
|
+
pg_get_triggerdef(t.oid),
|
|
2064
|
+
quote_ident(source_schema),
|
|
2065
|
+
quote_ident(dest_schema)
|
|
2066
|
+
) || ';' AS trig_ddl
|
|
2067
|
+
FROM pg_trigger t,
|
|
2068
|
+
pg_class c,
|
|
2069
|
+
pg_namespace n,
|
|
2070
|
+
pg_proc p
|
|
2071
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
2072
|
+
AND n.oid = c.relnamespace
|
|
2073
|
+
AND c.relkind in ('r', 'p')
|
|
2074
|
+
AND n.oid = p.pronamespace
|
|
2075
|
+
AND c.oid = t.tgrelid
|
|
2076
|
+
AND p.oid = t.tgfoid
|
|
2077
|
+
ORDER BY c.relname,
|
|
2078
|
+
t.tgname LOOP BEGIN cnt := cnt + 1;
|
|
2079
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2080
|
+
arec.trig_ddl;
|
|
2081
|
+
ELSE EXECUTE arec.trig_ddl;
|
|
2082
|
+
END IF;
|
|
2083
|
+
END;
|
|
2084
|
+
END LOOP;
|
|
2085
|
+
RAISE NOTICE ' TRIGGERS cloned: %',
|
|
2086
|
+
LPAD(cnt::text, 5, ' ');
|
|
2087
|
+
-- MV: Create Rules
|
|
2088
|
+
-- Fixes Issue#59 Implement Rules
|
|
2089
|
+
action := 'Rules';
|
|
2090
|
+
cnt := 0;
|
|
2091
|
+
FOR arec IN
|
|
2092
|
+
SELECT regexp_replace(definition, E'[\\n\\r]+', ' ', 'g') as definition
|
|
2093
|
+
FROM pg_rules
|
|
2094
|
+
WHERE schemaname = quote_ident(source_schema) LOOP cnt := cnt + 1;
|
|
2095
|
+
buffer := REPLACE(
|
|
2096
|
+
arec.definition,
|
|
2097
|
+
quote_ident(source_schema) || '.',
|
|
2098
|
+
quote_ident(dest_schema) || '.'
|
|
2099
|
+
);
|
|
2100
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2101
|
+
buffer;
|
|
2102
|
+
ELSE EXECUTE buffer;
|
|
2103
|
+
END IF;
|
|
2104
|
+
END LOOP;
|
|
2105
|
+
RAISE NOTICE ' RULES cloned: %',
|
|
2106
|
+
LPAD(cnt::text, 5, ' ');
|
|
2107
|
+
-- MV: Create Policies
|
|
2108
|
+
-- Fixes Issue#66 Implement Security policies for RLS
|
|
2109
|
+
action := 'Policies';
|
|
2110
|
+
cnt := 0;
|
|
2111
|
+
-- #106 Handle 9.6 which doesn't have "permissive"
|
|
2112
|
+
IF sq_server_version_num > 90624 THEN FOR arec IN -- Issue#78 FIX: handle case-sensitive names with quote_ident() on policy, tablename
|
|
2113
|
+
SELECT schemaname as schemaname,
|
|
2114
|
+
tablename as tablename,
|
|
2115
|
+
'CREATE POLICY ' || policyname || ' ON ' || quote_ident(dest_schema) || '.' || quote_ident(tablename) || ' AS ' || permissive || ' FOR ' || cmd || ' TO ' || array_to_string(roles, ',', '*') || ' USING (' || regexp_replace(qual, E'[\\n\\r]+', ' ', 'g') || ')' || CASE
|
|
2116
|
+
WHEN with_check IS NOT NULL THEN ' WITH CHECK ('
|
|
2117
|
+
ELSE ''
|
|
2118
|
+
END || coalesce(with_check, '') || CASE
|
|
2119
|
+
WHEN with_check IS NOT NULL THEN ');'
|
|
2120
|
+
ELSE ';'
|
|
2121
|
+
END as definition
|
|
2122
|
+
FROM pg_policies
|
|
2123
|
+
WHERE schemaname = quote_ident(source_schema)
|
|
2124
|
+
ORDER BY policyname LOOP cnt := cnt + 1;
|
|
2125
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2126
|
+
arec.definition;
|
|
2127
|
+
ELSE EXECUTE arec.definition;
|
|
2128
|
+
END IF;
|
|
2129
|
+
-- Issue#76: Enable row security if indicated
|
|
2130
|
+
SELECT c.relrowsecurity INTO abool
|
|
2131
|
+
FROM pg_class c,
|
|
2132
|
+
pg_namespace n
|
|
2133
|
+
where n.nspname = quote_ident(arec.schemaname)
|
|
2134
|
+
AND n.oid = c.relnamespace
|
|
2135
|
+
AND c.relname = quote_ident(arec.tablename)
|
|
2136
|
+
and c.relkind = 'r';
|
|
2137
|
+
IF abool THEN buffer = 'ALTER TABLE ' || dest_schema || '.' || arec.tablename || ' ENABLE ROW LEVEL SECURITY;';
|
|
2138
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2139
|
+
buffer;
|
|
2140
|
+
ELSE EXECUTE buffer;
|
|
2141
|
+
END IF;
|
|
2142
|
+
END IF;
|
|
2143
|
+
END LOOP;
|
|
2144
|
+
ELSE -- handle 9.6 versions
|
|
2145
|
+
FOR arec IN -- Issue#78 FIX: handle case-sensitive names with quote_ident() on policy, tablename
|
|
2146
|
+
SELECT schemaname as schemaname,
|
|
2147
|
+
tablename as tablename,
|
|
2148
|
+
'CREATE POLICY ' || policyname || ' ON ' || quote_ident(dest_schema) || '.' || quote_ident(tablename) || ' FOR ' || cmd || ' TO ' || array_to_string(roles, ',', '*') || ' USING (' || regexp_replace(qual, E'[\\n\\r]+', ' ', 'g') || ')' || CASE
|
|
2149
|
+
WHEN with_check IS NOT NULL THEN ' WITH CHECK ('
|
|
2150
|
+
ELSE ''
|
|
2151
|
+
END || coalesce(with_check, '') || CASE
|
|
2152
|
+
WHEN with_check IS NOT NULL THEN ');'
|
|
2153
|
+
ELSE ';'
|
|
2154
|
+
END as definition
|
|
2155
|
+
FROM pg_policies
|
|
2156
|
+
WHERE schemaname = quote_ident(source_schema)
|
|
2157
|
+
ORDER BY policyname LOOP cnt := cnt + 1;
|
|
2158
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2159
|
+
arec.definition;
|
|
2160
|
+
ELSE EXECUTE arec.definition;
|
|
2161
|
+
END IF;
|
|
2162
|
+
-- Issue#76: Enable row security if indicated
|
|
2163
|
+
SELECT c.relrowsecurity INTO abool
|
|
2164
|
+
FROM pg_class c,
|
|
2165
|
+
pg_namespace n
|
|
2166
|
+
where n.nspname = quote_ident(arec.schemaname)
|
|
2167
|
+
AND n.oid = c.relnamespace
|
|
2168
|
+
AND c.relname = quote_ident(arec.tablename)
|
|
2169
|
+
and c.relkind = 'r';
|
|
2170
|
+
IF abool THEN buffer = 'ALTER TABLE ' || dest_schema || '.' || arec.tablename || ' ENABLE ROW LEVEL SECURITY;';
|
|
2171
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2172
|
+
buffer;
|
|
2173
|
+
ELSE EXECUTE buffer;
|
|
2174
|
+
END IF;
|
|
2175
|
+
END IF;
|
|
2176
|
+
END LOOP;
|
|
2177
|
+
END IF;
|
|
2178
|
+
RAISE NOTICE ' POLICIES cloned: %',
|
|
2179
|
+
LPAD(cnt::text, 5, ' ');
|
|
2180
|
+
-- MJV Fixed #62 for comments (PASS 1)
|
|
2181
|
+
action := 'Comments1';
|
|
2182
|
+
cnt := 0;
|
|
2183
|
+
FOR qry IN -- Issue#74 Fix: Change schema from source to target. Also, do not include comments on foreign tables since we do not clone foreign tables at this time.
|
|
2184
|
+
SELECT 'COMMENT ON ' || CASE
|
|
2185
|
+
WHEN c.relkind in ('r', 'p')
|
|
2186
|
+
AND a.attname IS NULL THEN 'TABLE '
|
|
2187
|
+
WHEN c.relkind in ('r', 'p')
|
|
2188
|
+
AND a.attname IS NOT NULL THEN 'COLUMN '
|
|
2189
|
+
WHEN c.relkind = 'f' THEN 'FOREIGN TABLE '
|
|
2190
|
+
WHEN c.relkind = 'm' THEN 'MATERIALIZED VIEW '
|
|
2191
|
+
WHEN c.relkind = 'v' THEN 'VIEW '
|
|
2192
|
+
WHEN c.relkind = 'i' THEN 'INDEX '
|
|
2193
|
+
WHEN c.relkind = 'S' THEN 'SEQUENCE '
|
|
2194
|
+
ELSE 'XX'
|
|
2195
|
+
END || quote_ident(dest_schema) || '.' || CASE
|
|
2196
|
+
WHEN c.relkind in ('r', 'p')
|
|
2197
|
+
AND -- Issue#78: handle case-sensitive names with quote_ident()
|
|
2198
|
+
a.attname IS NOT NULL THEN quote_ident(c.relname) || '.' || a.attname
|
|
2199
|
+
ELSE quote_ident(c.relname)
|
|
2200
|
+
END || -- Issue#74 Fix
|
|
2201
|
+
-- ' IS ''' || d.description || ''';' as ddl
|
|
2202
|
+
' IS ' || quote_literal(d.description) || ';' as ddl
|
|
2203
|
+
FROM pg_class c
|
|
2204
|
+
JOIN pg_namespace n ON (n.oid = c.relnamespace)
|
|
2205
|
+
LEFT JOIN pg_description d ON (c.oid = d.objoid)
|
|
2206
|
+
LEFT JOIN pg_attribute a ON (
|
|
2207
|
+
c.oid = a.attrelid
|
|
2208
|
+
AND a.attnum > 0
|
|
2209
|
+
and a.attnum = d.objsubid
|
|
2210
|
+
)
|
|
2211
|
+
WHERE c.relkind <> 'f'
|
|
2212
|
+
AND d.description IS NOT NULL
|
|
2213
|
+
AND n.nspname = quote_ident(source_schema)
|
|
2214
|
+
ORDER BY ddl LOOP cnt := cnt + 1;
|
|
2215
|
+
-- BAD : "COMMENT ON SEQUENCE sample_clone2.CaseSensitive_ID_seq IS 'just a comment on CaseSensitive sequence';"
|
|
2216
|
+
-- GOOD: "COMMENT ON SEQUENCE "CaseSensitive_ID_seq" IS 'just a comment on CaseSensitive sequence';"
|
|
2217
|
+
-- Issue#98 For MVs we create comments when we create the MVs
|
|
2218
|
+
IF substring(qry, 1, 28) = 'COMMENT ON MATERIALIZED VIEW' THEN IF bDebug THEN RAISE NOTICE 'DEBUG: deferring comments on MVs';
|
|
2219
|
+
END IF;
|
|
2220
|
+
cnt = cnt - 1;
|
|
2221
|
+
continue;
|
|
2222
|
+
END IF;
|
|
2223
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2224
|
+
qry;
|
|
2225
|
+
ELSE EXECUTE qry;
|
|
2226
|
+
END IF;
|
|
2227
|
+
END LOOP;
|
|
2228
|
+
RAISE NOTICE ' COMMENTS(1) cloned: %',
|
|
2229
|
+
LPAD(cnt::text, 5, ' ');
|
|
2230
|
+
-- MJV Fixed #62 for comments (PASS 2)
|
|
2231
|
+
action := 'Comments2';
|
|
2232
|
+
cnt2 := 0;
|
|
2233
|
+
IF is_prokind THEN FOR qry IN -- Issue#74 Fix: Change schema from source to target.
|
|
2234
|
+
SELECT 'COMMENT ON SCHEMA ' || dest_schema || -- Issue#74 Fix
|
|
2235
|
+
-- ' IS ''' || d.description || ''';' as ddl
|
|
2236
|
+
' IS ' || quote_literal(d.description) || ';' as ddl
|
|
2237
|
+
from pg_namespace n,
|
|
2238
|
+
pg_description d
|
|
2239
|
+
where d.objoid = n.oid
|
|
2240
|
+
and n.nspname = quote_ident(source_schema)
|
|
2241
|
+
UNION
|
|
2242
|
+
-- Issue#74 Fix: need to replace source schema inline
|
|
2243
|
+
-- SELECT 'COMMENT ON TYPE ' || pg_catalog.format_type(t.oid, NULL) || ' IS ''' || pg_catalog.obj_description(t.oid, 'pg_type') || ''';' as ddl
|
|
2244
|
+
SELECT 'COMMENT ON TYPE ' || REPLACE(
|
|
2245
|
+
pg_catalog.format_type(t.oid, NULL),
|
|
2246
|
+
quote_ident(source_schema),
|
|
2247
|
+
quote_ident(dest_schema)
|
|
2248
|
+
) || ' IS ''' || pg_catalog.obj_description(t.oid, 'pg_type') || ''';' as ddl
|
|
2249
|
+
FROM pg_catalog.pg_type t
|
|
2250
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
2251
|
+
WHERE (
|
|
2252
|
+
t.typrelid = 0
|
|
2253
|
+
OR (
|
|
2254
|
+
SELECT c.relkind = 'c'
|
|
2255
|
+
FROM pg_catalog.pg_class c
|
|
2256
|
+
WHERE c.oid = t.typrelid
|
|
2257
|
+
)
|
|
2258
|
+
)
|
|
2259
|
+
AND NOT EXISTS(
|
|
2260
|
+
SELECT 1
|
|
2261
|
+
FROM pg_catalog.pg_type el
|
|
2262
|
+
WHERE el.oid = t.typelem
|
|
2263
|
+
AND el.typarray = t.oid
|
|
2264
|
+
)
|
|
2265
|
+
AND n.nspname = quote_ident(source_schema) COLLATE pg_catalog.default
|
|
2266
|
+
AND pg_catalog.obj_description(t.oid, 'pg_type') IS NOT NULL
|
|
2267
|
+
and t.typtype = 'c'
|
|
2268
|
+
UNION
|
|
2269
|
+
-- Issue#78: handle case-sensitive names with quote_ident()
|
|
2270
|
+
SELECT 'COMMENT ON COLLATION ' || quote_ident(dest_schema) || '.' || quote_ident(c.collname) || ' IS ''' || pg_catalog.obj_description(c.oid, 'pg_collation') || ''';' as ddl
|
|
2271
|
+
FROM pg_catalog.pg_collation c,
|
|
2272
|
+
pg_catalog.pg_namespace n
|
|
2273
|
+
WHERE n.oid = c.collnamespace
|
|
2274
|
+
AND c.collencoding IN (
|
|
2275
|
+
-1,
|
|
2276
|
+
pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding())
|
|
2277
|
+
)
|
|
2278
|
+
AND n.nspname = quote_ident(source_schema) COLLATE pg_catalog.default
|
|
2279
|
+
AND pg_catalog.obj_description(c.oid, 'pg_collation') IS NOT NULL
|
|
2280
|
+
UNION
|
|
2281
|
+
SELECT 'COMMENT ON ' || CASE
|
|
2282
|
+
WHEN p.prokind = 'f' THEN 'FUNCTION '
|
|
2283
|
+
WHEN p.prokind = 'p' THEN 'PROCEDURE '
|
|
2284
|
+
WHEN p.prokind = 'a' THEN 'AGGREGATE '
|
|
2285
|
+
END || dest_schema || '.' || p.proname || ' (' || oidvectortypes(p.proargtypes) || ')' -- Issue#74 Fix
|
|
2286
|
+
-- ' IS ''' || d.description || ''';' as ddl
|
|
2287
|
+
' IS ' || quote_literal(d.description) || ';' as ddl
|
|
2288
|
+
FROM pg_catalog.pg_namespace n
|
|
2289
|
+
JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid
|
|
2290
|
+
JOIN pg_description d ON (d.objoid = p.oid)
|
|
2291
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
2292
|
+
UNION
|
|
2293
|
+
SELECT 'COMMENT ON POLICY ' || p1.policyname || ' ON ' || dest_schema || '.' || p1.tablename || -- Issue#74 Fix
|
|
2294
|
+
-- ' IS ''' || d.description || ''';' as ddl
|
|
2295
|
+
' IS ' || quote_literal(d.description) || ';' as ddl
|
|
2296
|
+
FROM pg_policies p1,
|
|
2297
|
+
pg_policy p2,
|
|
2298
|
+
pg_class c,
|
|
2299
|
+
pg_namespace n,
|
|
2300
|
+
pg_description d
|
|
2301
|
+
WHERE p1.schemaname = n.nspname
|
|
2302
|
+
AND p1.tablename = c.relname
|
|
2303
|
+
AND n.oid = c.relnamespace
|
|
2304
|
+
AND c.relkind in ('r', 'p')
|
|
2305
|
+
AND p1.policyname = p2.polname
|
|
2306
|
+
AND d.objoid = p2.oid
|
|
2307
|
+
AND p1.schemaname = quote_ident(source_schema)
|
|
2308
|
+
UNION
|
|
2309
|
+
SELECT 'COMMENT ON DOMAIN ' || dest_schema || '.' || t.typname || -- Issue#74 Fix
|
|
2310
|
+
-- ' IS ''' || d.description || ''';' as ddl
|
|
2311
|
+
' IS ' || quote_literal(d.description) || ';' as ddl
|
|
2312
|
+
FROM pg_catalog.pg_type t
|
|
2313
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
2314
|
+
JOIN pg_catalog.pg_description d ON d.classoid = t.tableoid
|
|
2315
|
+
AND d.objoid = t.oid
|
|
2316
|
+
AND d.objsubid = 0
|
|
2317
|
+
WHERE t.typtype = 'd'
|
|
2318
|
+
AND n.nspname = quote_ident(source_schema) COLLATE pg_catalog.default
|
|
2319
|
+
ORDER BY 1 LOOP cnt2 := cnt2 + 1;
|
|
2320
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2321
|
+
qry;
|
|
2322
|
+
ELSE EXECUTE qry;
|
|
2323
|
+
END IF;
|
|
2324
|
+
END LOOP;
|
|
2325
|
+
ELSE -- must be v 10 or less
|
|
2326
|
+
FOR qry IN -- Issue#74 Fix: Change schema from source to target.
|
|
2327
|
+
SELECT 'COMMENT ON SCHEMA ' || dest_schema || -- Issue#74 Fix
|
|
2328
|
+
-- ' IS ''' || d.description || ''';' as ddl
|
|
2329
|
+
' IS ' || quote_literal(d.description) || ';' as ddl
|
|
2330
|
+
from pg_namespace n,
|
|
2331
|
+
pg_description d
|
|
2332
|
+
where d.objoid = n.oid
|
|
2333
|
+
and n.nspname = quote_ident(source_schema)
|
|
2334
|
+
UNION
|
|
2335
|
+
-- Issue#74 Fix: need to replace source schema inline
|
|
2336
|
+
-- SELECT 'COMMENT ON TYPE ' || pg_catalog.format_type(t.oid, NULL) || ' IS ''' || pg_catalog.obj_description(t.oid, 'pg_type') || ''';' as ddl
|
|
2337
|
+
SELECT 'COMMENT ON TYPE ' || REPLACE(
|
|
2338
|
+
pg_catalog.format_type(t.oid, NULL),
|
|
2339
|
+
quote_ident(source_schema),
|
|
2340
|
+
quote_ident(dest_schema)
|
|
2341
|
+
) || ' IS ''' || pg_catalog.obj_description(t.oid, 'pg_type') || ''';' as ddl
|
|
2342
|
+
FROM pg_catalog.pg_type t
|
|
2343
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
2344
|
+
WHERE (
|
|
2345
|
+
t.typrelid = 0
|
|
2346
|
+
OR (
|
|
2347
|
+
SELECT c.relkind = 'c'
|
|
2348
|
+
FROM pg_catalog.pg_class c
|
|
2349
|
+
WHERE c.oid = t.typrelid
|
|
2350
|
+
)
|
|
2351
|
+
)
|
|
2352
|
+
AND NOT EXISTS(
|
|
2353
|
+
SELECT 1
|
|
2354
|
+
FROM pg_catalog.pg_type el
|
|
2355
|
+
WHERE el.oid = t.typelem
|
|
2356
|
+
AND el.typarray = t.oid
|
|
2357
|
+
)
|
|
2358
|
+
AND n.nspname = quote_ident(source_schema) COLLATE pg_catalog.default
|
|
2359
|
+
AND pg_catalog.obj_description(t.oid, 'pg_type') IS NOT NULL
|
|
2360
|
+
and t.typtype = 'c'
|
|
2361
|
+
UNION
|
|
2362
|
+
-- FIX Isse#87 by adding double quotes around collation name
|
|
2363
|
+
SELECT 'COMMENT ON COLLATION ' || dest_schema || '."' || c.collname || '" IS ''' || pg_catalog.obj_description(c.oid, 'pg_collation') || ''';' as ddl
|
|
2364
|
+
FROM pg_catalog.pg_collation c,
|
|
2365
|
+
pg_catalog.pg_namespace n
|
|
2366
|
+
WHERE n.oid = c.collnamespace
|
|
2367
|
+
AND c.collencoding IN (
|
|
2368
|
+
-1,
|
|
2369
|
+
pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding())
|
|
2370
|
+
)
|
|
2371
|
+
AND n.nspname = quote_ident(source_schema) COLLATE pg_catalog.default
|
|
2372
|
+
AND pg_catalog.obj_description(c.oid, 'pg_collation') IS NOT NULL
|
|
2373
|
+
UNION
|
|
2374
|
+
SELECT 'COMMENT ON ' || CASE
|
|
2375
|
+
WHEN proisagg THEN 'AGGREGATE '
|
|
2376
|
+
ELSE 'FUNCTION '
|
|
2377
|
+
END || dest_schema || '.' || p.proname || ' (' || oidvectortypes(p.proargtypes) || ')' -- Issue#74 Fix
|
|
2378
|
+
-- ' IS ''' || d.description || ''';' as ddl
|
|
2379
|
+
' IS ' || quote_literal(d.description) || ';' as ddl
|
|
2380
|
+
FROM pg_catalog.pg_namespace n
|
|
2381
|
+
JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid
|
|
2382
|
+
JOIN pg_description d ON (d.objoid = p.oid)
|
|
2383
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
2384
|
+
UNION
|
|
2385
|
+
SELECT 'COMMENT ON POLICY ' || p1.policyname || ' ON ' || dest_schema || '.' || p1.tablename || -- Issue#74 Fix
|
|
2386
|
+
-- ' IS ''' || d.description || ''';' as ddl
|
|
2387
|
+
' IS ' || quote_literal(d.description) || ';' as ddl
|
|
2388
|
+
FROM pg_policies p1,
|
|
2389
|
+
pg_policy p2,
|
|
2390
|
+
pg_class c,
|
|
2391
|
+
pg_namespace n,
|
|
2392
|
+
pg_description d
|
|
2393
|
+
WHERE p1.schemaname = n.nspname
|
|
2394
|
+
AND p1.tablename = c.relname
|
|
2395
|
+
AND n.oid = c.relnamespace
|
|
2396
|
+
AND c.relkind in ('r', 'p')
|
|
2397
|
+
AND p1.policyname = p2.polname
|
|
2398
|
+
AND d.objoid = p2.oid
|
|
2399
|
+
AND p1.schemaname = quote_ident(source_schema)
|
|
2400
|
+
UNION
|
|
2401
|
+
SELECT 'COMMENT ON DOMAIN ' || dest_schema || '.' || t.typname || -- Issue#74 Fix
|
|
2402
|
+
-- ' IS ''' || d.description || ''';' as ddl
|
|
2403
|
+
' IS ' || quote_literal(d.description) || ';' as ddl
|
|
2404
|
+
FROM pg_catalog.pg_type t
|
|
2405
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
2406
|
+
JOIN pg_catalog.pg_description d ON d.classoid = t.tableoid
|
|
2407
|
+
AND d.objoid = t.oid
|
|
2408
|
+
AND d.objsubid = 0
|
|
2409
|
+
WHERE t.typtype = 'd'
|
|
2410
|
+
AND n.nspname = quote_ident(source_schema) COLLATE pg_catalog.default
|
|
2411
|
+
ORDER BY 1 LOOP cnt2 := cnt2 + 1;
|
|
2412
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2413
|
+
qry;
|
|
2414
|
+
ELSE EXECUTE qry;
|
|
2415
|
+
END IF;
|
|
2416
|
+
END LOOP;
|
|
2417
|
+
END IF;
|
|
2418
|
+
RAISE NOTICE ' COMMENTS(2) cloned: %',
|
|
2419
|
+
LPAD(cnt2::text, 5, ' ');
|
|
2420
|
+
-- Issue#95 bypass if No ACL specified.
|
|
2421
|
+
IF NOT bNoACL THEN -- ---------------------
|
|
2422
|
+
-- MV: Permissions: Defaults
|
|
2423
|
+
-- ---------------------
|
|
2424
|
+
EXECUTE 'SET search_path = ' || quote_ident(source_schema);
|
|
2425
|
+
action := 'PRIVS: Defaults';
|
|
2426
|
+
cnt := 0;
|
|
2427
|
+
FOR arec IN
|
|
2428
|
+
SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS "owner",
|
|
2429
|
+
n.nspname AS schema,
|
|
2430
|
+
CASE
|
|
2431
|
+
d.defaclobjtype
|
|
2432
|
+
WHEN 'r' THEN 'table'
|
|
2433
|
+
WHEN 'S' THEN 'sequence'
|
|
2434
|
+
WHEN 'f' THEN 'function'
|
|
2435
|
+
WHEN 'T' THEN 'type'
|
|
2436
|
+
WHEN 'n' THEN 'schema'
|
|
2437
|
+
END AS atype,
|
|
2438
|
+
d.defaclacl as defaclacl,
|
|
2439
|
+
pg_catalog.array_to_string(d.defaclacl, ',') as defaclstr
|
|
2440
|
+
FROM pg_catalog.pg_default_acl d
|
|
2441
|
+
LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = d.defaclnamespace)
|
|
2442
|
+
WHERE n.nspname IS NOT NULL
|
|
2443
|
+
AND n.nspname = quote_ident(source_schema)
|
|
2444
|
+
ORDER BY 3,
|
|
2445
|
+
2,
|
|
2446
|
+
1 LOOP BEGIN -- RAISE NOTICE ' owner=% type=% defaclacl=% defaclstr=%', arec.owner, arec.atype, arec.defaclacl, arec.defaclstr;
|
|
2447
|
+
FOREACH aclstr IN ARRAY arec.defaclacl LOOP cnt := cnt + 1;
|
|
2448
|
+
-- RAISE NOTICE ' aclstr=%', aclstr;
|
|
2449
|
+
-- break up into grantor, grantee, and privs, mydb_update=rwU/mydb_owner
|
|
2450
|
+
SELECT split_part(aclstr, '=', 1) INTO grantee;
|
|
2451
|
+
SELECT split_part(aclstr, '=', 2) INTO grantor;
|
|
2452
|
+
SELECT split_part(grantor, '/', 1) INTO privs;
|
|
2453
|
+
SELECT split_part(grantor, '/', 2) INTO grantor;
|
|
2454
|
+
-- RAISE NOTICE ' grantor=% grantee=% privs=%', grantor, grantee, privs;
|
|
2455
|
+
IF arec.atype = 'function' THEN -- Just having execute is enough to grant all apparently.
|
|
2456
|
+
buffer := 'ALTER DEFAULT PRIVILEGES FOR ROLE ' || grantor || ' IN SCHEMA ' || quote_ident(dest_schema) || ' GRANT ALL ON FUNCTIONS TO "' || grantee || '";';
|
|
2457
|
+
-- Issue#92 Fix
|
|
2458
|
+
-- set role = cm_stage_ro_grp;
|
|
2459
|
+
-- ALTER DEFAULT PRIVILEGES FOR ROLE cm_stage_ro_grp IN SCHEMA cm_stage GRANT REFERENCES, TRIGGER ON TABLES TO cm_stage_ro_grp;
|
|
2460
|
+
IF grantor = grantee THEN -- append set role to statement
|
|
2461
|
+
buffer = 'SET ROLE = ' || grantor || '; ' || buffer;
|
|
2462
|
+
END IF;
|
|
2463
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2464
|
+
buffer;
|
|
2465
|
+
ELSE EXECUTE buffer;
|
|
2466
|
+
END IF;
|
|
2467
|
+
-- Issue#92 Fix:
|
|
2468
|
+
EXECUTE 'SET ROLE = ' || calleruser;
|
|
2469
|
+
ELSIF arec.atype = 'sequence' THEN IF POSITION('r' IN privs) > 0
|
|
2470
|
+
AND POSITION('w' IN privs) > 0
|
|
2471
|
+
AND POSITION('U' IN privs) > 0 THEN -- arU is enough for all privs
|
|
2472
|
+
buffer := 'ALTER DEFAULT PRIVILEGES FOR ROLE ' || grantor || ' IN SCHEMA ' || quote_ident(dest_schema) || ' GRANT ALL ON SEQUENCES TO "' || grantee || '";';
|
|
2473
|
+
-- Issue#92 Fix
|
|
2474
|
+
IF grantor = grantee THEN -- append set role to statement
|
|
2475
|
+
buffer = 'SET ROLE = ' || grantor || '; ' || buffer;
|
|
2476
|
+
END IF;
|
|
2477
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2478
|
+
buffer;
|
|
2479
|
+
ELSE EXECUTE buffer;
|
|
2480
|
+
END IF;
|
|
2481
|
+
-- Issue#92 Fix:
|
|
2482
|
+
EXECUTE 'SET ROLE = ' || calleruser;
|
|
2483
|
+
ELSE -- have to specify each priv individually
|
|
2484
|
+
buffer2 := '';
|
|
2485
|
+
IF POSITION('r' IN privs) > 0 THEN buffer2 := 'SELECT';
|
|
2486
|
+
END IF;
|
|
2487
|
+
IF POSITION('w' IN privs) > 0 THEN IF buffer2 = '' THEN buffer2 := 'UPDATE';
|
|
2488
|
+
ELSE buffer2 := buffer2 || ', UPDATE';
|
|
2489
|
+
END IF;
|
|
2490
|
+
END IF;
|
|
2491
|
+
IF POSITION('U' IN privs) > 0 THEN IF buffer2 = '' THEN buffer2 := 'USAGE';
|
|
2492
|
+
ELSE buffer2 := buffer2 || ', USAGE';
|
|
2493
|
+
END IF;
|
|
2494
|
+
END IF;
|
|
2495
|
+
buffer := 'ALTER DEFAULT PRIVILEGES FOR ROLE ' || grantor || ' IN SCHEMA ' || quote_ident(dest_schema) || ' GRANT ' || buffer2 || ' ON SEQUENCES TO "' || grantee || '";';
|
|
2496
|
+
-- Issue#92 Fix
|
|
2497
|
+
IF grantor = grantee THEN -- append set role to statement
|
|
2498
|
+
buffer = 'SET ROLE = ' || grantor || '; ' || buffer;
|
|
2499
|
+
END IF;
|
|
2500
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2501
|
+
buffer;
|
|
2502
|
+
ELSE EXECUTE buffer;
|
|
2503
|
+
END IF;
|
|
2504
|
+
select current_user into buffer;
|
|
2505
|
+
-- Issue#92 Fix:
|
|
2506
|
+
EXECUTE 'SET ROLE = ' || calleruser;
|
|
2507
|
+
END IF;
|
|
2508
|
+
ELSIF arec.atype = 'table' THEN -- do each priv individually, jeeeesh!
|
|
2509
|
+
buffer2 := '';
|
|
2510
|
+
IF POSITION('a' IN privs) > 0 THEN buffer2 := 'INSERT';
|
|
2511
|
+
END IF;
|
|
2512
|
+
IF POSITION('r' IN privs) > 0 THEN IF buffer2 = '' THEN buffer2 := 'SELECT';
|
|
2513
|
+
ELSE buffer2 := buffer2 || ', SELECT';
|
|
2514
|
+
END IF;
|
|
2515
|
+
END IF;
|
|
2516
|
+
IF POSITION('w' IN privs) > 0 THEN IF buffer2 = '' THEN buffer2 := 'UPDATE';
|
|
2517
|
+
ELSE buffer2 := buffer2 || ', UPDATE';
|
|
2518
|
+
END IF;
|
|
2519
|
+
END IF;
|
|
2520
|
+
IF POSITION('d' IN privs) > 0 THEN IF buffer2 = '' THEN buffer2 := 'DELETE';
|
|
2521
|
+
ELSE buffer2 := buffer2 || ', DELETE';
|
|
2522
|
+
END IF;
|
|
2523
|
+
END IF;
|
|
2524
|
+
IF POSITION('t' IN privs) > 0 THEN IF buffer2 = '' THEN buffer2 := 'TRIGGER';
|
|
2525
|
+
ELSE buffer2 := buffer2 || ', TRIGGER';
|
|
2526
|
+
END IF;
|
|
2527
|
+
END IF;
|
|
2528
|
+
IF POSITION('T' IN privs) > 0 THEN IF buffer2 = '' THEN buffer2 := 'TRUNCATE';
|
|
2529
|
+
ELSE buffer2 := buffer2 || ', TRUNCATE';
|
|
2530
|
+
END IF;
|
|
2531
|
+
END IF;
|
|
2532
|
+
buffer := 'ALTER DEFAULT PRIVILEGES FOR ROLE ' || grantor || ' IN SCHEMA ' || quote_ident(dest_schema) || ' GRANT ' || buffer2 || ' ON TABLES TO "' || grantee || '";';
|
|
2533
|
+
-- Issue#92 Fix
|
|
2534
|
+
IF grantor = grantee THEN -- append set role to statement
|
|
2535
|
+
buffer = 'SET ROLE = ' || grantor || '; ' || buffer;
|
|
2536
|
+
END IF;
|
|
2537
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2538
|
+
buffer;
|
|
2539
|
+
ELSE EXECUTE buffer;
|
|
2540
|
+
END IF;
|
|
2541
|
+
select current_user into buffer;
|
|
2542
|
+
-- Issue#92 Fix:
|
|
2543
|
+
EXECUTE 'SET ROLE = ' || calleruser;
|
|
2544
|
+
ELSIF arec.atype = 'type' THEN IF POSITION('r' IN privs) > 0
|
|
2545
|
+
AND POSITION('w' IN privs) > 0
|
|
2546
|
+
AND POSITION('U' IN privs) > 0 THEN -- arU is enough for all privs
|
|
2547
|
+
buffer := 'ALTER DEFAULT PRIVILEGES FOR ROLE ' || grantor || ' IN SCHEMA ' || quote_ident(dest_schema) || ' GRANT ALL ON TYPES TO "' || grantee || '";';
|
|
2548
|
+
-- Issue#92 Fix
|
|
2549
|
+
IF grantor = grantee THEN -- append set role to statement
|
|
2550
|
+
buffer = 'SET ROLE = ' || grantor || '; ' || buffer;
|
|
2551
|
+
END IF;
|
|
2552
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2553
|
+
buffer;
|
|
2554
|
+
ELSE EXECUTE buffer;
|
|
2555
|
+
END IF;
|
|
2556
|
+
-- Issue#92 Fix:
|
|
2557
|
+
EXECUTE 'SET ROLE = ' || calleruser;
|
|
2558
|
+
ELSIF POSITION('U' IN privs) THEN buffer := 'ALTER DEFAULT PRIVILEGES FOR ROLE ' || grantor || ' IN SCHEMA ' || quote_ident(dest_schema) || ' GRANT USAGE ON TYPES TO "' || grantee || '";';
|
|
2559
|
+
-- Issue#92 Fix
|
|
2560
|
+
IF grantor = grantee THEN -- append set role to statement
|
|
2561
|
+
buffer = 'SET ROLE = ' || grantor || '; ' || buffer;
|
|
2562
|
+
END IF;
|
|
2563
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2564
|
+
buffer;
|
|
2565
|
+
ELSE EXECUTE buffer;
|
|
2566
|
+
END IF;
|
|
2567
|
+
-- Issue#92 Fix:
|
|
2568
|
+
EXECUTE 'SET ROLE = ' || calleruser;
|
|
2569
|
+
ELSE RAISE WARNING 'Unhandled TYPE Privs:: type=% privs=% owner=% defaclacl=% defaclstr=% grantor=% grantee=% ',
|
|
2570
|
+
arec.atype,
|
|
2571
|
+
privs,
|
|
2572
|
+
arec.owner,
|
|
2573
|
+
arec.defaclacl,
|
|
2574
|
+
arec.defaclstr,
|
|
2575
|
+
grantor,
|
|
2576
|
+
grantee;
|
|
2577
|
+
END IF;
|
|
2578
|
+
ELSE RAISE WARNING 'Unhandled Privs:: type=% privs=% owner=% defaclacl=% defaclstr=% grantor=% grantee=% ',
|
|
2579
|
+
arec.atype,
|
|
2580
|
+
privs,
|
|
2581
|
+
arec.owner,
|
|
2582
|
+
arec.defaclacl,
|
|
2583
|
+
arec.defaclstr,
|
|
2584
|
+
grantor,
|
|
2585
|
+
grantee;
|
|
2586
|
+
END IF;
|
|
2587
|
+
END LOOP;
|
|
2588
|
+
END;
|
|
2589
|
+
END LOOP;
|
|
2590
|
+
RAISE NOTICE ' DFLT PRIVS cloned: %',
|
|
2591
|
+
LPAD(cnt::text, 5, ' ');
|
|
2592
|
+
END IF;
|
|
2593
|
+
-- NO ACL BRANCH
|
|
2594
|
+
-- Issue#95 bypass if No ACL specified
|
|
2595
|
+
IF NOT bNoACL THEN -- MV: PRIVS: schema
|
|
2596
|
+
-- crunchy data extension, check_access
|
|
2597
|
+
-- SELECT role_path, base_role, as_role, objtype, schemaname, objname, array_to_string(array_agg(privname),',') as privs FROM all_access()
|
|
2598
|
+
-- WHERE base_role != CURRENT_USER and objtype = 'schema' and schemaname = 'public' group by 1,2,3,4,5,6;
|
|
2599
|
+
action := 'PRIVS: Schema';
|
|
2600
|
+
cnt := 0;
|
|
2601
|
+
FOR arec IN
|
|
2602
|
+
SELECT 'GRANT ' || p.perm::perm_type || ' ON SCHEMA ' || quote_ident(dest_schema) || ' TO "' || r.rolname || '";' as schema_ddl
|
|
2603
|
+
FROM pg_catalog.pg_namespace AS n
|
|
2604
|
+
CROSS JOIN pg_catalog.pg_roles AS r
|
|
2605
|
+
CROSS JOIN (
|
|
2606
|
+
VALUES ('USAGE'),
|
|
2607
|
+
('CREATE')
|
|
2608
|
+
) AS p(perm)
|
|
2609
|
+
WHERE n.nspname = quote_ident(source_schema)
|
|
2610
|
+
AND NOT r.rolsuper
|
|
2611
|
+
AND has_schema_privilege(r.oid, n.oid, p.perm)
|
|
2612
|
+
ORDER BY r.rolname,
|
|
2613
|
+
p.perm::perm_type LOOP BEGIN cnt := cnt + 1;
|
|
2614
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2615
|
+
arec.schema_ddl;
|
|
2616
|
+
ELSE EXECUTE arec.schema_ddl;
|
|
2617
|
+
END IF;
|
|
2618
|
+
END;
|
|
2619
|
+
END LOOP;
|
|
2620
|
+
RAISE NOTICE 'SCHEMA PRIVS cloned: %',
|
|
2621
|
+
LPAD(cnt::text, 5, ' ');
|
|
2622
|
+
END IF;
|
|
2623
|
+
-- NO ACL BRANCH
|
|
2624
|
+
-- Issue#95 bypass if No ACL specified
|
|
2625
|
+
IF NOT bNoACL THEN -- MV: PRIVS: sequences
|
|
2626
|
+
action := 'PRIVS: Sequences';
|
|
2627
|
+
cnt := 0;
|
|
2628
|
+
FOR arec IN -- Issue#78 FIX: handle case-sensitive names with quote_ident() on t.relname
|
|
2629
|
+
SELECT 'GRANT ' || p.perm::perm_type || ' ON ' || quote_ident(dest_schema) || '.' || quote_ident(t.relname::text) || ' TO "' || r.rolname || '";' as seq_ddl
|
|
2630
|
+
FROM pg_catalog.pg_class AS t
|
|
2631
|
+
CROSS JOIN pg_catalog.pg_roles AS r
|
|
2632
|
+
CROSS JOIN (
|
|
2633
|
+
VALUES ('SELECT'),
|
|
2634
|
+
('USAGE'),
|
|
2635
|
+
('UPDATE')
|
|
2636
|
+
) AS p(perm)
|
|
2637
|
+
WHERE t.relnamespace::regnamespace::name = quote_ident(source_schema)
|
|
2638
|
+
AND t.relkind = 'S'
|
|
2639
|
+
AND NOT r.rolsuper
|
|
2640
|
+
AND has_sequence_privilege(r.oid, t.oid, p.perm) LOOP BEGIN cnt := cnt + 1;
|
|
2641
|
+
-- IF bDebug THEN RAISE NOTICE 'DEBUG: ddl=%', arec.seq_ddl; END IF;
|
|
2642
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2643
|
+
arec.seq_ddl;
|
|
2644
|
+
ELSE EXECUTE arec.seq_ddl;
|
|
2645
|
+
END IF;
|
|
2646
|
+
END;
|
|
2647
|
+
END LOOP;
|
|
2648
|
+
RAISE NOTICE ' SEQ. PRIVS cloned: %',
|
|
2649
|
+
LPAD(cnt::text, 5, ' ');
|
|
2650
|
+
END IF;
|
|
2651
|
+
-- NO ACL BRANCH
|
|
2652
|
+
-- Issue#95 bypass if No ACL specified
|
|
2653
|
+
IF NOT bNoACL THEN -- MV: PRIVS: functions
|
|
2654
|
+
action := 'PRIVS: Functions/Procedures';
|
|
2655
|
+
cnt := 0;
|
|
2656
|
+
-- Issue#61 FIX: use set_config for empty string
|
|
2657
|
+
-- SET search_path = '';
|
|
2658
|
+
SELECT set_config('search_path', '', false) into v_dummy;
|
|
2659
|
+
-- RAISE NOTICE ' source_schema=% dest_schema=%',source_schema, dest_schema;
|
|
2660
|
+
FOR arec IN -- 2021-03-05 MJV FIX: issue#35: caused exception in some functions with parameters and gave privileges to other users that should not have gotten them.
|
|
2661
|
+
-- SELECT 'GRANT EXECUTE ON FUNCTION ' || quote_ident(dest_schema) || '.' || replace(regexp_replace(f.oid::regprocedure::text, '^((("[^"]*")|([^"][^.]*))\.)?', ''), source_schema, dest_schema) || ' TO "' || r.rolname || '";' as func_ddl
|
|
2662
|
+
-- FROM pg_catalog.pg_proc f CROSS JOIN pg_catalog.pg_roles AS r WHERE f.pronamespace::regnamespace::name = quote_ident(source_schema) AND NOT r.rolsuper AND has_function_privilege(r.oid, f.oid, 'EXECUTE')
|
|
2663
|
+
-- order by regexp_replace(f.oid::regprocedure::text, '^((("[^"]*")|([^"][^.]*))\.)?', '')
|
|
2664
|
+
-- 2021-03-05 MJV FIX: issue#37: defaults cause problems, use system function that returns args WITHOUT DEFAULTS
|
|
2665
|
+
-- COALESCE(r.routine_type, 'FUNCTION'): for aggregate functions, information_schema.routines contains NULL as routine_type value.
|
|
2666
|
+
-- Issue#78 FIX: handle case-sensitive names with quote_ident() on rp.routine_name
|
|
2667
|
+
SELECT 'GRANT ' || rp.privilege_type || ' ON ' || COALESCE(r.routine_type, 'FUNCTION') || ' ' || quote_ident(dest_schema) || '.' || quote_ident(rp.routine_name) || ' (' || pg_get_function_identity_arguments(p.oid) || ') TO ' || string_agg(distinct rp.grantee, ',') || ';' as func_dcl
|
|
2668
|
+
FROM information_schema.routine_privileges rp,
|
|
2669
|
+
information_schema.routines r,
|
|
2670
|
+
pg_proc p,
|
|
2671
|
+
pg_namespace n
|
|
2672
|
+
WHERE rp.routine_schema = quote_ident(source_schema)
|
|
2673
|
+
AND rp.is_grantable = 'YES'
|
|
2674
|
+
AND rp.routine_schema = r.routine_schema
|
|
2675
|
+
AND rp.routine_name = r.routine_name
|
|
2676
|
+
AND rp.routine_schema = n.nspname
|
|
2677
|
+
AND n.oid = p.pronamespace
|
|
2678
|
+
AND p.proname = r.routine_name
|
|
2679
|
+
GROUP BY rp.privilege_type,
|
|
2680
|
+
r.routine_type,
|
|
2681
|
+
rp.routine_name,
|
|
2682
|
+
pg_get_function_identity_arguments(p.oid) LOOP BEGIN cnt := cnt + 1;
|
|
2683
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2684
|
+
arec.func_dcl;
|
|
2685
|
+
ELSE EXECUTE arec.func_dcl;
|
|
2686
|
+
END IF;
|
|
2687
|
+
END;
|
|
2688
|
+
END LOOP;
|
|
2689
|
+
EXECUTE 'SET search_path = ' || quote_ident(source_schema);
|
|
2690
|
+
RAISE NOTICE ' FUNC PRIVS cloned: %',
|
|
2691
|
+
LPAD(cnt::text, 5, ' ');
|
|
2692
|
+
END IF;
|
|
2693
|
+
-- NO ACL BRANCH
|
|
2694
|
+
-- Issue#95 bypass if No ACL specified
|
|
2695
|
+
IF NOT bNoACL THEN -- MV: PRIVS: tables
|
|
2696
|
+
action := 'PRIVS: Tables';
|
|
2697
|
+
-- regular, partitioned, and foreign tables plus view and materialized view permissions. Ignored for now: implement foreign table defs.
|
|
2698
|
+
cnt := 0;
|
|
2699
|
+
FOR arec IN -- SELECT 'GRANT ' || p.perm::perm_type || CASE WHEN t.relkind in ('r', 'p', 'f') THEN ' ON TABLE ' WHEN t.relkind in ('v', 'm') THEN ' ON ' END || quote_ident(dest_schema) || '.' || t.relname::text || ' TO "' || r.rolname || '";' as tbl_ddl,
|
|
2700
|
+
-- has_table_privilege(r.oid, t.oid, p.perm) AS granted, t.relkind
|
|
2701
|
+
-- FROM pg_catalog.pg_class AS t CROSS JOIN pg_catalog.pg_roles AS r CROSS JOIN (VALUES (TEXT 'SELECT'), ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) AS p(perm)
|
|
2702
|
+
-- WHERE t.relnamespace::regnamespace::name = quote_ident(source_schema) AND t.relkind in ('r', 'p', 'f', 'v', 'm') AND NOT r.rolsuper AND has_table_privilege(r.oid, t.oid, p.perm) order by t.relname::text, t.relkind
|
|
2703
|
+
-- 2021-03-05 MJV FIX: Fixed Issue#36 for tables
|
|
2704
|
+
SELECT c.relkind,
|
|
2705
|
+
'GRANT ' || tb.privilege_type || CASE
|
|
2706
|
+
WHEN c.relkind in ('r', 'p') THEN ' ON TABLE '
|
|
2707
|
+
WHEN c.relkind in ('v', 'm') THEN ' ON '
|
|
2708
|
+
END || -- Issue#78 FIX: handle case-sensitive names with quote_ident() on t.relname
|
|
2709
|
+
-- Issue#108 FIX: enclose double-quote grantees with special characters
|
|
2710
|
+
-- quote_ident(dest_schema) || '.' || quote_ident(tb.table_name) || ' TO ' || string_agg(tb.grantee, ',') || ';' as tbl_dcl
|
|
2711
|
+
quote_ident(dest_schema) || '.' || quote_ident(tb.table_name) || ' TO ' || string_agg('"' || tb.grantee || '"', ',') || ';' as tbl_dcl
|
|
2712
|
+
FROM information_schema.table_privileges tb,
|
|
2713
|
+
pg_class c,
|
|
2714
|
+
pg_namespace n
|
|
2715
|
+
WHERE tb.table_schema = quote_ident(source_schema)
|
|
2716
|
+
AND tb.table_name = c.relname
|
|
2717
|
+
AND c.relkind in ('r', 'p', 'v', 'm')
|
|
2718
|
+
AND c.relnamespace = n.oid
|
|
2719
|
+
AND n.nspname = quote_ident(source_schema)
|
|
2720
|
+
GROUP BY c.relkind,
|
|
2721
|
+
tb.privilege_type,
|
|
2722
|
+
tb.table_schema,
|
|
2723
|
+
tb.table_name LOOP BEGIN cnt := cnt + 1;
|
|
2724
|
+
-- IF bDebug THEN RAISE NOTICE 'DEBUG: ddl=%', arec.tbl_dcl; END IF;
|
|
2725
|
+
-- Issue#46. Fixed reference to invalid record name (tbl_ddl --> tbl_dcl).
|
|
2726
|
+
IF arec.relkind = 'f' THEN RAISE WARNING 'Foreign tables are not currently implemented, so skipping privs for them. ddl=%',
|
|
2727
|
+
arec.tbl_dcl;
|
|
2728
|
+
ELSE IF bDDLOnly THEN RAISE INFO '%',
|
|
2729
|
+
arec.tbl_dcl;
|
|
2730
|
+
ELSE EXECUTE arec.tbl_dcl;
|
|
2731
|
+
END IF;
|
|
2732
|
+
END IF;
|
|
2733
|
+
END;
|
|
2734
|
+
END LOOP;
|
|
2735
|
+
RAISE NOTICE ' TABLE PRIVS cloned: %',
|
|
2736
|
+
LPAD(cnt::text, 5, ' ');
|
|
2737
|
+
END IF;
|
|
2738
|
+
-- NO ACL BRANCH
|
|
2739
|
+
-- LOOP for regular tables and populate them if specified
|
|
2740
|
+
-- Issue#75 moved from big table loop above to here.
|
|
2741
|
+
IF bData THEN r = clock_timestamp();
|
|
2742
|
+
-- IF bVerbose THEN RAISE NOTICE 'START: copy rows %',clock_timestamp() - t; END IF;
|
|
2743
|
+
IF bVerbose THEN RAISE NOTICE 'Copying rows...';
|
|
2744
|
+
END IF;
|
|
2745
|
+
EXECUTE 'SET search_path = ' || quote_ident(dest_schema);
|
|
2746
|
+
action := 'Copy Rows';
|
|
2747
|
+
FOREACH tblelement IN ARRAY tblarray LOOP s = clock_timestamp();
|
|
2748
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG1: no UDTs %',
|
|
2749
|
+
tblelement;
|
|
2750
|
+
END IF;
|
|
2751
|
+
EXECUTE tblelement;
|
|
2752
|
+
GET DIAGNOSTICS cnt = ROW_COUNT;
|
|
2753
|
+
buffer = substring(tblelement, 13);
|
|
2754
|
+
SELECT POSITION(' OVERRIDING SYSTEM VALUE SELECT ' IN buffer) INTO cnt2;
|
|
2755
|
+
IF cnt2 = 0 THEN
|
|
2756
|
+
SELECT POSITION(' SELECT ' IN buffer) INTO cnt2;
|
|
2757
|
+
buffer = substring(buffer, 1, cnt2);
|
|
2758
|
+
ELSE buffer = substring(buffer, 1, cnt2);
|
|
2759
|
+
END IF;
|
|
2760
|
+
SELECT RPAD(buffer, 35, ' ') INTO buffer;
|
|
2761
|
+
cnt2 := cast(
|
|
2762
|
+
extract(
|
|
2763
|
+
epoch
|
|
2764
|
+
from (clock_timestamp() - s)
|
|
2765
|
+
) as numeric(18, 3)
|
|
2766
|
+
);
|
|
2767
|
+
IF bVerbose THEN RAISE NOTICE 'Populated cloned table, % Rows Copied: % seconds: %',
|
|
2768
|
+
buffer,
|
|
2769
|
+
LPAD(cnt::text, 10, ' '),
|
|
2770
|
+
LPAD(cnt2::text, 5, ' ');
|
|
2771
|
+
END IF;
|
|
2772
|
+
tblscopied := tblscopied + 1;
|
|
2773
|
+
END LOOP;
|
|
2774
|
+
-- Issue#79 implementation
|
|
2775
|
+
-- Do same for tables with user-defined elements using copy to file method
|
|
2776
|
+
FOREACH tblelement IN ARRAY tblarray2 LOOP s = clock_timestamp();
|
|
2777
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG2: UDTs %',
|
|
2778
|
+
tblelement;
|
|
2779
|
+
END IF;
|
|
2780
|
+
EXECUTE tblelement;
|
|
2781
|
+
GET DIAGNOSTICS cnt = ROW_COUNT;
|
|
2782
|
+
-- STATEMENT LOOKS LIKE THIS:
|
|
2783
|
+
-- INSERT INTO sample11.warehouses SELECT * FROM sample.warehouses;
|
|
2784
|
+
-- INSERT INTO sample11.person OVERRIDING SYSTEM VALUE SELECT * FROM sample.person;
|
|
2785
|
+
-- COPY sample.address TO '/tmp/cloneschema.tmp' WITH DELIMITER AS ',';\
|
|
2786
|
+
buffer = TRIM(tblelement::text);
|
|
2787
|
+
-- RAISE NOTICE 'element=%', buffer;
|
|
2788
|
+
cnt1 = POSITION('INSERT INTO' IN buffer);
|
|
2789
|
+
cnt2 = POSITION('COPY ' IN buffer);
|
|
2790
|
+
IF cnt1 > 0 THEN buffer = substring(buffer, 12);
|
|
2791
|
+
ELSIF cnt2 > 0 THEN buffer = substring(buffer, 5);
|
|
2792
|
+
ELSE RAISE EXCEPTION 'Programming Error for parsing tblarray2.';
|
|
2793
|
+
END IF;
|
|
2794
|
+
-- RAISE NOTICE 'buffer1=%', buffer;
|
|
2795
|
+
cnt1 = POSITION(' OVERRIDING ' IN buffer);
|
|
2796
|
+
cnt2 = POSITION('SELECT * FROM ' IN buffer);
|
|
2797
|
+
cnt3 = POSITION(' FROM ' IN buffer);
|
|
2798
|
+
cnt4 = POSITION(' TO ' IN buffer);
|
|
2799
|
+
IF cnt1 > 0 THEN buffer = substring(buffer, 1, cnt1 -2);
|
|
2800
|
+
ELSIF cnt2 > 0 THEN buffer = substring(buffer, 1, cnt2 -2);
|
|
2801
|
+
ELSIF cnt3 > 0 THEN buffer = substring(buffer, 1, cnt3 -1);
|
|
2802
|
+
ELSIF cnt4 > 0 THEN -- skip the COPY TO statements
|
|
2803
|
+
continue;
|
|
2804
|
+
ELSE RAISE EXCEPTION 'Programming Error for parsing tblarray2.';
|
|
2805
|
+
END IF;
|
|
2806
|
+
-- RAISE NOTICE 'buffer2=%', buffer;
|
|
2807
|
+
SELECT RPAD(buffer, 35, ' ') INTO buffer;
|
|
2808
|
+
-- RAISE NOTICE 'buffer3=%', buffer;
|
|
2809
|
+
cnt2 := cast(
|
|
2810
|
+
extract(
|
|
2811
|
+
epoch
|
|
2812
|
+
from (clock_timestamp() - s)
|
|
2813
|
+
) as numeric(18, 3)
|
|
2814
|
+
);
|
|
2815
|
+
IF bVerbose THEN RAISE NOTICE 'Populated cloned table, % Rows Copied: % seconds: %',
|
|
2816
|
+
buffer,
|
|
2817
|
+
LPAD(cnt::text, 10, ' '),
|
|
2818
|
+
LPAD(cnt2::text, 5, ' ');
|
|
2819
|
+
END IF;
|
|
2820
|
+
tblscopied := tblscopied + 1;
|
|
2821
|
+
END LOOP;
|
|
2822
|
+
-- Issue#101
|
|
2823
|
+
-- Do same for tables with user-defined elements using direct method with text cast
|
|
2824
|
+
FOREACH tblelement IN ARRAY tblarray3 LOOP s = clock_timestamp();
|
|
2825
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG3: UDTs %',
|
|
2826
|
+
tblelement;
|
|
2827
|
+
END IF;
|
|
2828
|
+
EXECUTE tblelement;
|
|
2829
|
+
GET DIAGNOSTICS cnt = ROW_COUNT;
|
|
2830
|
+
cnt2 = POSITION(' (' IN tblelement::text);
|
|
2831
|
+
IF cnt2 > 0 THEN buffer = substring(tblelement, 1, cnt2);
|
|
2832
|
+
buffer = substring(buffer, 6);
|
|
2833
|
+
SELECT RPAD(buffer, 35, ' ') INTO buffer;
|
|
2834
|
+
cnt2 := cast(
|
|
2835
|
+
extract(
|
|
2836
|
+
epoch
|
|
2837
|
+
from (clock_timestamp() - s)
|
|
2838
|
+
) as numeric(18, 3)
|
|
2839
|
+
);
|
|
2840
|
+
IF bVerbose THEN RAISE NOTICE 'Populated cloned table, % Rows Copied: % seconds: %',
|
|
2841
|
+
buffer,
|
|
2842
|
+
LPAD(cnt::text, 10, ' '),
|
|
2843
|
+
LPAD(cnt2::text, 5, ' ');
|
|
2844
|
+
END IF;
|
|
2845
|
+
tblscopied := tblscopied + 1;
|
|
2846
|
+
END IF;
|
|
2847
|
+
END LOOP;
|
|
2848
|
+
-- Issue#98 MVs deferred until now
|
|
2849
|
+
FOREACH tblelement IN ARRAY mvarray LOOP s = clock_timestamp();
|
|
2850
|
+
EXECUTE tblelement;
|
|
2851
|
+
-- get diagnostics for MV creates or refreshes does not work, always returns 1
|
|
2852
|
+
GET DIAGNOSTICS cnt = ROW_COUNT;
|
|
2853
|
+
buffer = substring(tblelement, 25);
|
|
2854
|
+
cnt2 = POSITION(' AS ' IN buffer);
|
|
2855
|
+
IF cnt2 > 0 THEN buffer = substring(buffer, 1, cnt2);
|
|
2856
|
+
SELECT RPAD(buffer, 36, ' ') INTO buffer;
|
|
2857
|
+
cnt2 := cast(
|
|
2858
|
+
extract(
|
|
2859
|
+
epoch
|
|
2860
|
+
from (clock_timestamp() - s)
|
|
2861
|
+
) as numeric(18, 3)
|
|
2862
|
+
);
|
|
2863
|
+
IF bVerbose THEN RAISE NOTICE 'Populated Mat. View, % Rows Inserted: ? seconds: %',
|
|
2864
|
+
buffer,
|
|
2865
|
+
LPAD(cnt2::text, 5, ' ');
|
|
2866
|
+
END IF;
|
|
2867
|
+
mvscopied := mvscopied + 1;
|
|
2868
|
+
END IF;
|
|
2869
|
+
END LOOP;
|
|
2870
|
+
cnt := cast(
|
|
2871
|
+
extract(
|
|
2872
|
+
epoch
|
|
2873
|
+
from (clock_timestamp() - r)
|
|
2874
|
+
) as numeric(18, 3)
|
|
2875
|
+
);
|
|
2876
|
+
IF bVerbose THEN RAISE NOTICE 'Copy rows duration: % seconds',
|
|
2877
|
+
cnt;
|
|
2878
|
+
END IF;
|
|
2879
|
+
END IF;
|
|
2880
|
+
RAISE NOTICE ' TABLES copied: %',
|
|
2881
|
+
LPAD(tblscopied::text, 5, ' ');
|
|
2882
|
+
RAISE NOTICE ' MATVIEWS refreshed: %',
|
|
2883
|
+
LPAD(mvscopied::text, 5, ' ');
|
|
2884
|
+
-- Issue#78 forces us to defer FKeys until the end since we previously did row copies before FKeys
|
|
2885
|
+
-- add FK constraint
|
|
2886
|
+
action := 'FK Constraints';
|
|
2887
|
+
cnt := 0;
|
|
2888
|
+
-- Issue#61 FIX: use set_config for empty string
|
|
2889
|
+
-- SET search_path = '';
|
|
2890
|
+
SELECT set_config('search_path', '', false) into v_dummy;
|
|
2891
|
+
FOR qry IN
|
|
2892
|
+
SELECT 'ALTER TABLE ' || quote_ident(dest_schema) || '.' || quote_ident(rn.relname) || ' ADD CONSTRAINT ' || quote_ident(ct.conname) || ' ' || REPLACE(
|
|
2893
|
+
pg_get_constraintdef(ct.oid),
|
|
2894
|
+
'REFERENCES ' || quote_ident(source_schema) || '.',
|
|
2895
|
+
'REFERENCES ' || quote_ident(dest_schema) || '.'
|
|
2896
|
+
) || ';'
|
|
2897
|
+
FROM pg_constraint ct
|
|
2898
|
+
JOIN pg_class rn ON rn.oid = ct.conrelid -- Issue#103 needed to addd this left join
|
|
2899
|
+
LEFT JOIN pg_inherits i ON (rn.oid = i.inhrelid)
|
|
2900
|
+
WHERE connamespace = src_oid
|
|
2901
|
+
AND rn.relkind = 'r'
|
|
2902
|
+
AND ct.contype = 'f' -- Issue#103 fix: needed to also add this null check
|
|
2903
|
+
AND i.inhrelid is null LOOP cnt := cnt + 1;
|
|
2904
|
+
IF bDDLOnly THEN RAISE INFO '%',
|
|
2905
|
+
qry;
|
|
2906
|
+
ELSE IF bDebug THEN RAISE NOTICE 'DEBUG: adding FKEY constraint: %',
|
|
2907
|
+
qry;
|
|
2908
|
+
END IF;
|
|
2909
|
+
EXECUTE qry;
|
|
2910
|
+
END IF;
|
|
2911
|
+
END LOOP;
|
|
2912
|
+
EXECUTE 'SET search_path = ' || quote_ident(source_schema);
|
|
2913
|
+
RAISE NOTICE ' FKEYS cloned: %',
|
|
2914
|
+
LPAD(cnt::text, 5, ' ');
|
|
2915
|
+
IF src_path_old = ''
|
|
2916
|
+
OR src_path_old = '""' THEN -- RAISE NOTICE 'Restoring old search_path to empty string';
|
|
2917
|
+
SELECT set_config('search_path', '', false) into v_dummy;
|
|
2918
|
+
ELSE -- RAISE NOTICE 'Restoring old search_path to:%', src_path_old;
|
|
2919
|
+
EXECUTE 'SET search_path = ' || src_path_old;
|
|
2920
|
+
END IF;
|
|
2921
|
+
SELECT setting INTO v_dummy
|
|
2922
|
+
FROM pg_settings
|
|
2923
|
+
WHERE name = 'search_path';
|
|
2924
|
+
IF bDebug THEN RAISE NOTICE 'DEBUG: setting search_path back to what it was: %',
|
|
2925
|
+
v_dummy;
|
|
2926
|
+
END IF;
|
|
2927
|
+
cnt := cast(
|
|
2928
|
+
extract(
|
|
2929
|
+
epoch
|
|
2930
|
+
from (clock_timestamp() - t)
|
|
2931
|
+
) as numeric(18, 3)
|
|
2932
|
+
);
|
|
2933
|
+
IF bVerbose THEN RAISE NOTICE 'clone_schema duration: % seconds',
|
|
2934
|
+
cnt;
|
|
2935
|
+
END IF;
|
|
2936
|
+
EXCEPTION
|
|
2937
|
+
WHEN others THEN BEGIN GET STACKED DIAGNOSTICS v_diag1 = MESSAGE_TEXT,
|
|
2938
|
+
v_diag2 = PG_EXCEPTION_DETAIL,
|
|
2939
|
+
v_diag3 = PG_EXCEPTION_HINT,
|
|
2940
|
+
v_diag4 = RETURNED_SQLSTATE,
|
|
2941
|
+
v_diag5 = PG_CONTEXT,
|
|
2942
|
+
v_diag6 = PG_EXCEPTION_CONTEXT;
|
|
2943
|
+
v_ret := 'line=' || v_diag6 || '. ' || v_diag4 || '. ' || v_diag1;
|
|
2944
|
+
-- Issue#101: added version to exception output
|
|
2945
|
+
-- RAISE NOTICE 'v_diag1=% v_diag2=% v_diag3=% v_diag4=% v_diag5=% v_diag6=%', v_diag1, v_diag2, v_diag3, v_diag4, v_diag5, v_diag6;
|
|
2946
|
+
buffer2 = '';
|
|
2947
|
+
IF action = 'Copy Rows'
|
|
2948
|
+
AND v_diag4 = '42704' THEN -- Issue#105 Help user to fix the problem.
|
|
2949
|
+
buffer2 = 'It appears you have a USER-DEFINED column type mismatch. Try running clone_schema with the FILECOPY option. ';
|
|
2950
|
+
END IF;
|
|
2951
|
+
IF lastsql <> '' THEN buffer = v_ret || E'\n' || buffer2 || E'\n' || lastsql;
|
|
2952
|
+
ELSE buffer = v_ret || E'\n' || buffer2;
|
|
2953
|
+
END IF;
|
|
2954
|
+
RAISE EXCEPTION 'Version: % Action: % Diagnostics: %',
|
|
2955
|
+
v_version,
|
|
2956
|
+
action,
|
|
2957
|
+
buffer;
|
|
2958
|
+
IF src_path_old = '' THEN -- RAISE NOTICE 'setting old search_path to empty string';
|
|
2959
|
+
SELECT set_config('search_path', '', false);
|
|
2960
|
+
ELSE -- RAISE NOTICE 'setting old search_path to:%', src_path_old;
|
|
2961
|
+
EXECUTE 'SET search_path = ' || src_path_old;
|
|
2962
|
+
END IF;
|
|
2963
|
+
RETURN;
|
|
2964
|
+
END;
|
|
2965
|
+
RETURN;
|
|
2966
|
+
END;
|
|
2967
|
+
$_$;
|
|
2968
|
+
ALTER FUNCTION public.clone_schema(
|
|
2969
|
+
source_schema text,
|
|
2970
|
+
dest_schema text,
|
|
2971
|
+
VARIADIC arr public.cloneparms []
|
|
2972
|
+
) OWNER TO postgres;
|
|
2973
|
+
--
|
|
2974
|
+
-- Name: get_insert_stmt_ddl(text, text, text, boolean); Type: FUNCTION; Schema: public; Owner: postgres
|
|
2975
|
+
--
|
|
2976
|
+
|
|
2977
|
+
CREATE FUNCTION public.get_insert_stmt_ddl(
|
|
2978
|
+
source_schema text,
|
|
2979
|
+
target_schema text,
|
|
2980
|
+
atable text,
|
|
2981
|
+
btextcast boolean DEFAULT false
|
|
2982
|
+
) RETURNS text LANGUAGE plpgsql AS $$
|
|
2983
|
+
DECLARE -- the ddl we're building
|
|
2984
|
+
v_insert_ddl text := '';
|
|
2985
|
+
v_cols text := '';
|
|
2986
|
+
v_cols_sel text := '';
|
|
2987
|
+
v_cnt int := 0;
|
|
2988
|
+
v_colrec record;
|
|
2989
|
+
v_schema text;
|
|
2990
|
+
BEGIN FOR v_colrec IN
|
|
2991
|
+
SELECT c.column_name,
|
|
2992
|
+
c.data_type,
|
|
2993
|
+
c.udt_name,
|
|
2994
|
+
c.udt_schema,
|
|
2995
|
+
c.character_maximum_length,
|
|
2996
|
+
c.is_nullable,
|
|
2997
|
+
c.column_default,
|
|
2998
|
+
c.numeric_precision,
|
|
2999
|
+
c.numeric_scale,
|
|
3000
|
+
c.is_identity,
|
|
3001
|
+
c.identity_generation,
|
|
3002
|
+
c.is_generated
|
|
3003
|
+
FROM information_schema.columns c
|
|
3004
|
+
WHERE (table_schema, table_name) = (source_schema, atable)
|
|
3005
|
+
ORDER BY ordinal_position LOOP IF v_colrec.udt_schema = 'public' THEN v_schema = 'public';
|
|
3006
|
+
ELSE v_schema = target_schema;
|
|
3007
|
+
END IF;
|
|
3008
|
+
v_cnt = v_cnt + 1;
|
|
3009
|
+
IF v_colrec.is_identity = 'YES'
|
|
3010
|
+
OR v_colrec.is_generated = 'ALWAYS' THEN -- skip
|
|
3011
|
+
continue;
|
|
3012
|
+
END IF;
|
|
3013
|
+
IF v_colrec.data_type = 'USER-DEFINED' THEN IF v_cols = '' THEN v_cols = v_colrec.column_name;
|
|
3014
|
+
IF bTextCast THEN -- v_cols_sel = v_colrec.column_name || '::text::' || v_schema || '.' || v_colrec.udt_name;
|
|
3015
|
+
IF v_schema = 'public' THEN v_cols_sel = v_colrec.column_name || '::' || v_schema || '.' || v_colrec.udt_name;
|
|
3016
|
+
ELSE v_cols_sel = v_colrec.column_name || '::text::' || v_colrec.udt_name;
|
|
3017
|
+
END IF;
|
|
3018
|
+
ELSE v_cols_sel = v_colrec.column_name || '::' || v_schema || '.' || v_colrec.udt_name;
|
|
3019
|
+
END IF;
|
|
3020
|
+
ELSE v_cols = v_cols || ', ' || v_colrec.column_name;
|
|
3021
|
+
IF bTextCast THEN -- v_cols_sel = v_cols_sel || ', ' || v_colrec.column_name || '::text::' || v_schema || '.' || v_colrec.udt_name;
|
|
3022
|
+
IF v_schema = 'public' THEN v_cols_sel = v_cols_sel || ', ' || v_colrec.column_name || '::' || v_schema || '.' || v_colrec.udt_name;
|
|
3023
|
+
ELSE v_cols_sel = v_cols_sel || ', ' || v_colrec.column_name || '::text::' || v_colrec.udt_name;
|
|
3024
|
+
END IF;
|
|
3025
|
+
ELSE v_cols_sel = v_cols_sel || ', ' || v_colrec.column_name || '::' || v_schema || '.' || v_colrec.udt_name;
|
|
3026
|
+
END IF;
|
|
3027
|
+
END IF;
|
|
3028
|
+
ELSE IF v_cols = '' THEN v_cols = v_colrec.column_name;
|
|
3029
|
+
v_cols_sel = v_colrec.column_name;
|
|
3030
|
+
ELSE v_cols = v_cols || ', ' || v_colrec.column_name;
|
|
3031
|
+
v_cols_sel = v_cols_sel || ', ' || v_colrec.column_name;
|
|
3032
|
+
END IF;
|
|
3033
|
+
END IF;
|
|
3034
|
+
END LOOP;
|
|
3035
|
+
-- put it all together and return the insert statement
|
|
3036
|
+
-- INSERT INTO clone1.address2 (id2, id3, addr) SELECT id2::text::clone1.udt_myint, id3::text::clone1.udt_myint, addr FROM sample.address;
|
|
3037
|
+
v_insert_ddl = 'INSERT INTO ' || target_schema || '.' || atable || ' (' || v_cols || ') ' || 'SELECT ' || v_cols_sel || ' FROM ' || source_schema || '.' || atable || ';';
|
|
3038
|
+
RETURN v_insert_ddl;
|
|
3039
|
+
END;
|
|
3040
|
+
$$;
|
|
3041
|
+
ALTER FUNCTION public.get_insert_stmt_ddl(
|
|
3042
|
+
source_schema text,
|
|
3043
|
+
target_schema text,
|
|
3044
|
+
atable text,
|
|
3045
|
+
btextcast boolean
|
|
3046
|
+
) OWNER TO postgres;
|
|
3047
|
+
--
|
|
3048
|
+
-- Name: get_table_ddl(character varying, character varying, boolean); Type: FUNCTION; Schema: public; Owner: postgres
|
|
3049
|
+
--
|
|
3050
|
+
|
|
3051
|
+
CREATE FUNCTION public.get_table_ddl(
|
|
3052
|
+
in_schema character varying,
|
|
3053
|
+
in_table character varying,
|
|
3054
|
+
bfkeys boolean
|
|
3055
|
+
) RETURNS text LANGUAGE plpgsql AS $_$
|
|
3056
|
+
DECLARE -- the ddl we're building
|
|
3057
|
+
v_table_ddl text;
|
|
3058
|
+
-- data about the target table
|
|
3059
|
+
v_table_oid int;
|
|
3060
|
+
-- records for looping
|
|
3061
|
+
v_colrec record;
|
|
3062
|
+
v_constraintrec record;
|
|
3063
|
+
v_indexrec record;
|
|
3064
|
+
v_primary boolean := False;
|
|
3065
|
+
v_constraint_name text;
|
|
3066
|
+
v_src_path_old text := '';
|
|
3067
|
+
v_src_path_new text := '';
|
|
3068
|
+
v_dummy text;
|
|
3069
|
+
v_partbound text;
|
|
3070
|
+
v_pgversion int;
|
|
3071
|
+
v_parent text := '';
|
|
3072
|
+
v_relopts text := '';
|
|
3073
|
+
v_tablespace text;
|
|
3074
|
+
v_partition_key text := '';
|
|
3075
|
+
v_temp text;
|
|
3076
|
+
bPartitioned bool := False;
|
|
3077
|
+
bInheritance bool := False;
|
|
3078
|
+
bRelispartition bool;
|
|
3079
|
+
constraintarr text [] := '{}';
|
|
3080
|
+
constraintelement text;
|
|
3081
|
+
bSkip boolean;
|
|
3082
|
+
BEGIN
|
|
3083
|
+
SELECT c.oid,
|
|
3084
|
+
(
|
|
3085
|
+
SELECT setting
|
|
3086
|
+
FROM pg_settings
|
|
3087
|
+
WHERE name = 'server_version_num'
|
|
3088
|
+
) INTO v_table_oid,
|
|
3089
|
+
v_pgversion
|
|
3090
|
+
FROM pg_catalog.pg_class c
|
|
3091
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
3092
|
+
WHERE c.relkind IN ('r', 'p')
|
|
3093
|
+
AND c.relname = in_table
|
|
3094
|
+
AND n.nspname = in_schema;
|
|
3095
|
+
IF (v_table_oid IS NULL) THEN RAISE EXCEPTION 'table does not exist';
|
|
3096
|
+
END IF;
|
|
3097
|
+
-- get user-defined tablespaces if applicable
|
|
3098
|
+
SELECT TABLESPACE INTO v_temp
|
|
3099
|
+
FROM pg_tables
|
|
3100
|
+
WHERE schemaname = in_schema
|
|
3101
|
+
AND tablename = in_table
|
|
3102
|
+
AND TABLESPACE IS NOT NULL;
|
|
3103
|
+
-- Issue#99 Fix: simple coding error!
|
|
3104
|
+
-- IF v_tablespace IS NULL THEN
|
|
3105
|
+
IF v_temp IS NULL THEN v_tablespace := 'TABLESPACE pg_default';
|
|
3106
|
+
ELSE v_tablespace := 'TABLESPACE ' || v_temp;
|
|
3107
|
+
END IF;
|
|
3108
|
+
-- also see if there are any SET commands for this table, ie, autovacuum_enabled=off, fillfactor=70
|
|
3109
|
+
WITH relopts AS (
|
|
3110
|
+
SELECT unnest(c.reloptions) relopts
|
|
3111
|
+
FROM pg_class c,
|
|
3112
|
+
pg_namespace n
|
|
3113
|
+
WHERE n.nspname = in_schema
|
|
3114
|
+
AND n.oid = c.relnamespace
|
|
3115
|
+
AND c.relname = in_table
|
|
3116
|
+
)
|
|
3117
|
+
SELECT string_agg(r.relopts, ', ') AS relopts INTO v_temp
|
|
3118
|
+
FROM relopts r;
|
|
3119
|
+
IF v_temp IS NULL THEN v_relopts := '';
|
|
3120
|
+
ELSE v_relopts := ' WITH (' || v_temp || ')';
|
|
3121
|
+
END IF;
|
|
3122
|
+
-- Issue#61 FIX: set search_path = public before we do anything to force explicit schema qualification but dont forget to set it back before exiting...
|
|
3123
|
+
SELECT setting INTO v_src_path_old
|
|
3124
|
+
FROM pg_settings
|
|
3125
|
+
WHERE name = 'search_path';
|
|
3126
|
+
SELECT REPLACE(
|
|
3127
|
+
REPLACE(setting, '"$user"', '$user'),
|
|
3128
|
+
'$user',
|
|
3129
|
+
'"$user"'
|
|
3130
|
+
) INTO v_src_path_old
|
|
3131
|
+
FROM pg_settings
|
|
3132
|
+
WHERE name = 'search_path';
|
|
3133
|
+
-- RAISE INFO 'DEBUG tableddl: saving old search_path: ***%***', v_src_path_old;
|
|
3134
|
+
EXECUTE 'SET search_path = "public"';
|
|
3135
|
+
SELECT setting INTO v_src_path_new
|
|
3136
|
+
FROM pg_settings
|
|
3137
|
+
WHERE name = 'search_path';
|
|
3138
|
+
-- grab the oid of the table; https://www.postgresql.org/docs/8.3/catalog-pg-class.html
|
|
3139
|
+
SELECT c.oid INTO v_table_oid
|
|
3140
|
+
FROM pg_catalog.pg_class c
|
|
3141
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
3142
|
+
WHERE 1 = 1
|
|
3143
|
+
AND c.relkind = 'r'
|
|
3144
|
+
AND c.relname = in_table
|
|
3145
|
+
AND n.nspname = in_schema;
|
|
3146
|
+
IF (v_table_oid IS NULL) THEN -- Dont give up yet. It might be a partitioned table
|
|
3147
|
+
SELECT c.oid INTO v_table_oid
|
|
3148
|
+
FROM pg_catalog.pg_class c
|
|
3149
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
3150
|
+
WHERE 1 = 1
|
|
3151
|
+
AND c.relkind = 'p'
|
|
3152
|
+
AND c.relname = in_table
|
|
3153
|
+
AND n.nspname = in_schema;
|
|
3154
|
+
IF (v_table_oid IS NULL) THEN RAISE EXCEPTION 'table does not exist';
|
|
3155
|
+
END IF;
|
|
3156
|
+
bPartitioned := True;
|
|
3157
|
+
END IF;
|
|
3158
|
+
IF v_pgversion < 100000 THEN
|
|
3159
|
+
SELECT c2.relname parent INTO v_parent
|
|
3160
|
+
FROM pg_class c1,
|
|
3161
|
+
pg_namespace n,
|
|
3162
|
+
pg_inherits i,
|
|
3163
|
+
pg_class c2
|
|
3164
|
+
WHERE n.nspname = in_schema
|
|
3165
|
+
AND n.oid = c1.relnamespace
|
|
3166
|
+
AND c1.relname = in_table
|
|
3167
|
+
AND c1.oid = i.inhrelid
|
|
3168
|
+
AND i.inhparent = c2.oid
|
|
3169
|
+
AND c1.relkind = 'r';
|
|
3170
|
+
IF (v_parent IS NOT NULL) THEN bPartitioned := True;
|
|
3171
|
+
bInheritance := True;
|
|
3172
|
+
END IF;
|
|
3173
|
+
ELSE
|
|
3174
|
+
SELECT c2.relname parent,
|
|
3175
|
+
c1.relispartition,
|
|
3176
|
+
pg_get_expr(c1.relpartbound, c1.oid, TRUE) INTO v_parent,
|
|
3177
|
+
bRelispartition,
|
|
3178
|
+
v_partbound
|
|
3179
|
+
FROM pg_class c1,
|
|
3180
|
+
pg_namespace n,
|
|
3181
|
+
pg_inherits i,
|
|
3182
|
+
pg_class c2
|
|
3183
|
+
WHERE n.nspname = in_schema
|
|
3184
|
+
AND n.oid = c1.relnamespace
|
|
3185
|
+
AND c1.relname = in_table
|
|
3186
|
+
AND c1.oid = i.inhrelid
|
|
3187
|
+
AND i.inhparent = c2.oid
|
|
3188
|
+
AND c1.relkind = 'r';
|
|
3189
|
+
IF (v_parent IS NOT NULL) THEN bPartitioned := True;
|
|
3190
|
+
IF bRelispartition THEN bInheritance := False;
|
|
3191
|
+
ELSE bInheritance := True;
|
|
3192
|
+
END IF;
|
|
3193
|
+
END IF;
|
|
3194
|
+
END IF;
|
|
3195
|
+
-- RAISE NOTICE 'version=% schema=% parent=% relopts=% tablespace=% partitioned=% inherited=% relispartition=%',v_pgversion, in_schema, v_parent, v_relopts, v_tablespace, bPartitioned, bInheritance, bRelispartition;
|
|
3196
|
+
-- start the create definition
|
|
3197
|
+
v_table_ddl := 'CREATE TABLE ' || in_schema || '.' || in_table || ' (' || E'\n';
|
|
3198
|
+
-- define all of the columns in the table; https://stackoverflow.com/a/8153081/3068233
|
|
3199
|
+
FOR v_colrec IN
|
|
3200
|
+
SELECT c.column_name,
|
|
3201
|
+
c.data_type,
|
|
3202
|
+
c.udt_name,
|
|
3203
|
+
c.udt_schema,
|
|
3204
|
+
c.character_maximum_length,
|
|
3205
|
+
c.is_nullable,
|
|
3206
|
+
c.column_default,
|
|
3207
|
+
c.numeric_precision,
|
|
3208
|
+
c.numeric_scale,
|
|
3209
|
+
c.is_identity,
|
|
3210
|
+
c.identity_generation
|
|
3211
|
+
FROM information_schema.columns c
|
|
3212
|
+
WHERE (table_schema, table_name) = (in_schema, in_table)
|
|
3213
|
+
ORDER BY ordinal_position LOOP v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
|
|
3214
|
+
|| v_colrec.column_name || ' ' -- FIX #82, FIX #100 as well by adding 'citext' to the list
|
|
3215
|
+
-- FIX #105 by overriding the previous fixes (#82, #100), which presumed "public" was always the schema for extensions. It could be a custom schema.
|
|
3216
|
+
-- so assume udt_schema for all USER-DEFINED datatypes
|
|
3217
|
+
-- || CASE WHEN v_colrec.udt_name in ('geometry', 'box2d', 'box2df', 'box3d', 'geography', 'geometry_dump', 'gidx', 'spheroid', 'valid_detail','citext')
|
|
3218
|
+
-- THEN v_colrec.udt_name
|
|
3219
|
+
|| CASE
|
|
3220
|
+
WHEN v_colrec.data_type = 'USER-DEFINED' -- THEN in_schema || '.' || v_colrec.udt_name ELSE v_colrec.data_type END
|
|
3221
|
+
THEN v_colrec.udt_schema || '.' || v_colrec.udt_name
|
|
3222
|
+
ELSE v_colrec.data_type
|
|
3223
|
+
END || CASE
|
|
3224
|
+
WHEN v_colrec.is_identity = 'YES' THEN CASE
|
|
3225
|
+
WHEN v_colrec.identity_generation = 'ALWAYS' THEN ' GENERATED ALWAYS AS IDENTITY'
|
|
3226
|
+
ELSE ' GENERATED BY DEFAULT AS IDENTITY'
|
|
3227
|
+
END
|
|
3228
|
+
ELSE ''
|
|
3229
|
+
END || CASE
|
|
3230
|
+
WHEN v_colrec.character_maximum_length IS NOT NULL THEN ('(' || v_colrec.character_maximum_length || ')')
|
|
3231
|
+
WHEN v_colrec.numeric_precision > 0
|
|
3232
|
+
AND v_colrec.numeric_scale > 0 THEN '(' || v_colrec.numeric_precision || ',' || v_colrec.numeric_scale || ')'
|
|
3233
|
+
ELSE ''
|
|
3234
|
+
END || ' ' || CASE
|
|
3235
|
+
WHEN v_colrec.is_nullable = 'NO' THEN 'NOT NULL'
|
|
3236
|
+
ELSE 'NULL'
|
|
3237
|
+
END || CASE
|
|
3238
|
+
WHEN v_colrec.column_default IS NOT null THEN (' DEFAULT ' || v_colrec.column_default)
|
|
3239
|
+
ELSE ''
|
|
3240
|
+
END || ',' || E'\n';
|
|
3241
|
+
END LOOP;
|
|
3242
|
+
-- define all the constraints in the; https://www.postgresql.org/docs/9.1/catalog-pg-constraint.html && https://dba.stackexchange.com/a/214877/75296
|
|
3243
|
+
-- Issue#103: do not get foreign keys for partitions since they are defined on the parent and this will cause an "already exists" error otherwise
|
|
3244
|
+
-- Also conparentid is not in V10, so bypass since we do not have FKEYS in partitioned tables in V10
|
|
3245
|
+
IF v_pgversion < 110000 THEN FOR v_constraintrec IN
|
|
3246
|
+
SELECT con.conname as constraint_name,
|
|
3247
|
+
con.contype as constraint_type,
|
|
3248
|
+
CASE
|
|
3249
|
+
WHEN con.contype = 'p' THEN 1 -- primary key constraint
|
|
3250
|
+
WHEN con.contype = 'u' THEN 2 -- unique constraint
|
|
3251
|
+
WHEN con.contype = 'f' THEN 3 -- foreign key constraint
|
|
3252
|
+
WHEN con.contype = 'c' THEN 4
|
|
3253
|
+
ELSE 5
|
|
3254
|
+
END as type_rank,
|
|
3255
|
+
pg_get_constraintdef(con.oid) as constraint_definition
|
|
3256
|
+
FROM pg_catalog.pg_constraint con
|
|
3257
|
+
JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid
|
|
3258
|
+
JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace
|
|
3259
|
+
WHERE nsp.nspname = in_schema
|
|
3260
|
+
AND rel.relname = in_table
|
|
3261
|
+
ORDER BY type_rank LOOP -- Issue#85 fix
|
|
3262
|
+
-- constraintarr := constraintarr || v_constraintrec.constraint_name;
|
|
3263
|
+
constraintarr := constraintarr || v_constraintrec.constraint_name::text;
|
|
3264
|
+
IF v_constraintrec.type_rank = 1 THEN v_primary := True;
|
|
3265
|
+
v_constraint_name := v_constraintrec.constraint_name;
|
|
3266
|
+
END IF;
|
|
3267
|
+
IF NOT bfkeys
|
|
3268
|
+
AND v_constraintrec.constraint_type = 'f' THEN continue;
|
|
3269
|
+
END IF;
|
|
3270
|
+
v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
|
|
3271
|
+
|| 'CONSTRAINT' || ' ' || v_constraintrec.constraint_name || ' ' || v_constraintrec.constraint_definition || ',' || E'\n';
|
|
3272
|
+
END LOOP;
|
|
3273
|
+
ELSE FOR v_constraintrec IN
|
|
3274
|
+
SELECT con.conname as constraint_name,
|
|
3275
|
+
con.contype as constraint_type,
|
|
3276
|
+
CASE
|
|
3277
|
+
WHEN con.contype = 'p' THEN 1 -- primary key constraint
|
|
3278
|
+
WHEN con.contype = 'u' THEN 2 -- unique constraint
|
|
3279
|
+
WHEN con.contype = 'f' THEN 3 -- foreign key constraint
|
|
3280
|
+
WHEN con.contype = 'c' THEN 4
|
|
3281
|
+
ELSE 5
|
|
3282
|
+
END as type_rank,
|
|
3283
|
+
pg_get_constraintdef(con.oid) as constraint_definition
|
|
3284
|
+
FROM pg_catalog.pg_constraint con
|
|
3285
|
+
JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid
|
|
3286
|
+
JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace
|
|
3287
|
+
WHERE nsp.nspname = in_schema
|
|
3288
|
+
AND rel.relname = in_table -- Issue#103: do not get partitioned tables
|
|
3289
|
+
AND con.conparentid = 0
|
|
3290
|
+
ORDER BY type_rank LOOP -- Issue#85 fix
|
|
3291
|
+
-- constraintarr := constraintarr || v_constraintrec.constraint_name;
|
|
3292
|
+
constraintarr := constraintarr || v_constraintrec.constraint_name::text;
|
|
3293
|
+
IF v_constraintrec.type_rank = 1 THEN v_primary := True;
|
|
3294
|
+
v_constraint_name := v_constraintrec.constraint_name;
|
|
3295
|
+
END IF;
|
|
3296
|
+
IF NOT bfkeys
|
|
3297
|
+
AND v_constraintrec.constraint_type = 'f' THEN continue;
|
|
3298
|
+
END IF;
|
|
3299
|
+
v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
|
|
3300
|
+
|| 'CONSTRAINT' || ' ' || v_constraintrec.constraint_name || ' ' || v_constraintrec.constraint_definition || ',' || E'\n';
|
|
3301
|
+
END LOOP;
|
|
3302
|
+
END IF;
|
|
3303
|
+
-- drop the last comma before ending the create statement
|
|
3304
|
+
v_table_ddl = substr(v_table_ddl, 0, length(v_table_ddl) - 1) || E'\n';
|
|
3305
|
+
-- end the create table def but add inherits clause if valid
|
|
3306
|
+
IF bPartitioned
|
|
3307
|
+
and bInheritance THEN v_table_ddl := v_table_ddl || ') INHERITS (' || in_schema || '.' || v_parent || ') ' || v_relopts || ' ' || v_tablespace || ';' || E'\n';
|
|
3308
|
+
ELSIF v_pgversion >= 100000
|
|
3309
|
+
AND bPartitioned
|
|
3310
|
+
and NOT bInheritance THEN -- See if this is a partitioned table (pg_class.relkind = 'p') and add the partitioned key
|
|
3311
|
+
SELECT pg_get_partkeydef (c1.oid) AS partition_key INTO v_partition_key
|
|
3312
|
+
FROM pg_class c1
|
|
3313
|
+
JOIN pg_namespace n ON (n.oid = c1.relnamespace)
|
|
3314
|
+
LEFT JOIN pg_partitioned_table p ON (c1.oid = p.partrelid)
|
|
3315
|
+
WHERE n.nspname = in_schema
|
|
3316
|
+
AND n.oid = c1.relnamespace
|
|
3317
|
+
AND c1.relname = in_table
|
|
3318
|
+
AND c1.relkind = 'p';
|
|
3319
|
+
END IF;
|
|
3320
|
+
IF v_partition_key IS NOT NULL
|
|
3321
|
+
AND v_partition_key <> '' THEN -- add partition clause
|
|
3322
|
+
-- NOTE: cannot specify default tablespace for partitioned relations
|
|
3323
|
+
v_table_ddl := v_table_ddl || ') PARTITION BY ' || v_partition_key || ';' || E'\n';
|
|
3324
|
+
ELSIF bPartitioned
|
|
3325
|
+
AND not bInheritance THEN IF v_relopts <> '' THEN v_table_ddl := 'CREATE TABLE ' || in_schema || '.' || in_table || ' PARTITION OF ' || in_schema || '.' || v_parent || ' ' || v_partbound || v_relopts || ' ' || v_tablespace || '; ' || E'\n';
|
|
3326
|
+
ELSE v_table_ddl := 'CREATE TABLE ' || in_schema || '.' || in_table || ' PARTITION OF ' || in_schema || '.' || v_parent || ' ' || v_partbound || ' ' || v_tablespace || '; ' || E'\n';
|
|
3327
|
+
END IF;
|
|
3328
|
+
ELSIF bPartitioned
|
|
3329
|
+
and bInheritance THEN -- we already did this above
|
|
3330
|
+
v_table_ddl := v_table_ddl;
|
|
3331
|
+
ELSIF v_relopts <> '' THEN v_table_ddl := v_table_ddl || ') ' || v_relopts || ' ' || v_tablespace || ';' || E'\n';
|
|
3332
|
+
ELSE v_table_ddl := v_table_ddl || ') ' || v_tablespace || ';' || E'\n';
|
|
3333
|
+
END IF;
|
|
3334
|
+
-- suffix create statement with all of the indexes on the table
|
|
3335
|
+
FOR v_indexrec IN
|
|
3336
|
+
SELECT indexdef,
|
|
3337
|
+
indexname
|
|
3338
|
+
FROM pg_indexes
|
|
3339
|
+
WHERE (schemaname, tablename) = (in_schema, in_table) LOOP -- Issue#83 fix: loop through constraints and skip ones already defined
|
|
3340
|
+
bSkip = False;
|
|
3341
|
+
FOREACH constraintelement IN ARRAY constraintarr LOOP IF constraintelement = v_indexrec.indexname THEN bSkip = True;
|
|
3342
|
+
EXIT;
|
|
3343
|
+
END IF;
|
|
3344
|
+
END LOOP;
|
|
3345
|
+
if bSkip THEN CONTINUE;
|
|
3346
|
+
END IF;
|
|
3347
|
+
v_table_ddl := v_table_ddl || v_indexrec.indexdef || ';' || E'\n';
|
|
3348
|
+
END LOOP;
|
|
3349
|
+
-- reset search_path back to what it was
|
|
3350
|
+
IF v_src_path_old = '' THEN
|
|
3351
|
+
SELECT set_config('search_path', '', false) into v_dummy;
|
|
3352
|
+
ELSE EXECUTE 'SET search_path = ' || v_src_path_old;
|
|
3353
|
+
END IF;
|
|
3354
|
+
-- RAISE NOTICE 'DEBUG tableddl: reset search_path back to ***%***', v_src_path_old;
|
|
3355
|
+
-- return the ddl
|
|
3356
|
+
RETURN v_table_ddl;
|
|
3357
|
+
END;
|
|
3358
|
+
$_$;
|
|
3359
|
+
ALTER FUNCTION public.get_table_ddl(
|
|
3360
|
+
in_schema character varying,
|
|
3361
|
+
in_table character varying,
|
|
3362
|
+
bfkeys boolean
|
|
3363
|
+
) OWNER TO postgres;
|
|
3364
|
+
--
|
|
3365
|
+
-- Name: get_table_ddl_complex(text, text, text, integer); Type: FUNCTION; Schema: public; Owner: postgres
|
|
3366
|
+
--
|
|
3367
|
+
|
|
3368
|
+
CREATE FUNCTION public.get_table_ddl_complex(
|
|
3369
|
+
src_schema text,
|
|
3370
|
+
dst_schema text,
|
|
3371
|
+
in_table text,
|
|
3372
|
+
sq_server_version_num integer
|
|
3373
|
+
) RETURNS text LANGUAGE plpgsql AS $$
|
|
3374
|
+
DECLARE v_table_ddl text;
|
|
3375
|
+
v_buffer1 text;
|
|
3376
|
+
v_buffer2 text;
|
|
3377
|
+
BEGIN IF sq_server_version_num < 110000 THEN
|
|
3378
|
+
SELECT 'CREATE TABLE ' || quote_ident(dst_schema) || '.' || pc.relname || E'(\n' || string_agg(
|
|
3379
|
+
pa.attname || ' ' || pg_catalog.format_type(pa.atttypid, pa.atttypmod) || coalesce(
|
|
3380
|
+
' DEFAULT ' || (
|
|
3381
|
+
SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid)
|
|
3382
|
+
FROM pg_catalog.pg_attrdef d
|
|
3383
|
+
WHERE d.adrelid = pa.attrelid
|
|
3384
|
+
AND d.adnum = pa.attnum
|
|
3385
|
+
AND pa.atthasdef
|
|
3386
|
+
),
|
|
3387
|
+
''
|
|
3388
|
+
) || ' ' || CASE
|
|
3389
|
+
pa.attnotnull
|
|
3390
|
+
WHEN TRUE THEN 'NOT NULL'
|
|
3391
|
+
ELSE 'NULL'
|
|
3392
|
+
END,
|
|
3393
|
+
E',\n'
|
|
3394
|
+
) || coalesce(
|
|
3395
|
+
(
|
|
3396
|
+
SELECT E',\n' || string_agg(
|
|
3397
|
+
'CONSTRAINT ' || pc1.conname || ' ' || pg_get_constraintdef(pc1.oid),
|
|
3398
|
+
E',\n'
|
|
3399
|
+
ORDER BY pc1.conindid
|
|
3400
|
+
)
|
|
3401
|
+
FROM pg_constraint pc1 --Issue#103: do not return FKEYS for partitions since we assume it is implied by the one done on the parent table, otherwise error for trying to define it again.
|
|
3402
|
+
WHERE pc1.conrelid = pa.attrelid
|
|
3403
|
+
),
|
|
3404
|
+
''
|
|
3405
|
+
) INTO v_buffer1
|
|
3406
|
+
FROM pg_catalog.pg_attribute pa
|
|
3407
|
+
JOIN pg_catalog.pg_class pc ON pc.oid = pa.attrelid
|
|
3408
|
+
AND pc.relname = quote_ident(in_table)
|
|
3409
|
+
JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace
|
|
3410
|
+
AND pn.nspname = quote_ident(src_schema)
|
|
3411
|
+
WHERE pa.attnum > 0
|
|
3412
|
+
AND NOT pa.attisdropped
|
|
3413
|
+
GROUP BY pn.nspname,
|
|
3414
|
+
pc.relname,
|
|
3415
|
+
pa.attrelid;
|
|
3416
|
+
ELSE
|
|
3417
|
+
SELECT 'CREATE TABLE ' || quote_ident(dst_schema) || '.' || pc.relname || E'(\n' || string_agg(
|
|
3418
|
+
pa.attname || ' ' || pg_catalog.format_type(pa.atttypid, pa.atttypmod) || coalesce(
|
|
3419
|
+
' DEFAULT ' || (
|
|
3420
|
+
SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid)
|
|
3421
|
+
FROM pg_catalog.pg_attrdef d
|
|
3422
|
+
WHERE d.adrelid = pa.attrelid
|
|
3423
|
+
AND d.adnum = pa.attnum
|
|
3424
|
+
AND pa.atthasdef
|
|
3425
|
+
),
|
|
3426
|
+
''
|
|
3427
|
+
) || ' ' || CASE
|
|
3428
|
+
pa.attnotnull
|
|
3429
|
+
WHEN TRUE THEN 'NOT NULL'
|
|
3430
|
+
ELSE 'NULL'
|
|
3431
|
+
END,
|
|
3432
|
+
E',\n'
|
|
3433
|
+
) || coalesce(
|
|
3434
|
+
(
|
|
3435
|
+
SELECT E',\n' || string_agg(
|
|
3436
|
+
'CONSTRAINT ' || pc1.conname || ' ' || pg_get_constraintdef(pc1.oid),
|
|
3437
|
+
E',\n'
|
|
3438
|
+
ORDER BY pc1.conindid
|
|
3439
|
+
)
|
|
3440
|
+
FROM pg_constraint pc1 --Issue#103: do not return FKEYS for partitions since we assume it is implied by the one done on the parent table, otherwise error for trying to define it again.
|
|
3441
|
+
WHERE pc1.conrelid = pa.attrelid
|
|
3442
|
+
AND pc1.conparentid = 0
|
|
3443
|
+
),
|
|
3444
|
+
''
|
|
3445
|
+
) INTO v_buffer1
|
|
3446
|
+
FROM pg_catalog.pg_attribute pa
|
|
3447
|
+
JOIN pg_catalog.pg_class pc ON pc.oid = pa.attrelid
|
|
3448
|
+
AND pc.relname = quote_ident(in_table)
|
|
3449
|
+
JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace
|
|
3450
|
+
AND pn.nspname = quote_ident(src_schema)
|
|
3451
|
+
WHERE pa.attnum > 0
|
|
3452
|
+
AND NOT pa.attisdropped
|
|
3453
|
+
GROUP BY pn.nspname,
|
|
3454
|
+
pc.relname,
|
|
3455
|
+
pa.attrelid;
|
|
3456
|
+
END IF;
|
|
3457
|
+
-- append partition keyword to it
|
|
3458
|
+
SELECT pg_catalog.pg_get_partkeydef(c.oid::pg_catalog.oid) into v_buffer2
|
|
3459
|
+
FROM pg_catalog.pg_class c
|
|
3460
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
3461
|
+
WHERE c.relname = quote_ident(in_table) COLLATE pg_catalog.default
|
|
3462
|
+
AND n.nspname = quote_ident(src_schema) COLLATE pg_catalog.default;
|
|
3463
|
+
v_table_ddl := v_buffer1 || ') PARTITION BY ' || v_buffer2 || ';';
|
|
3464
|
+
RETURN v_table_ddl;
|
|
3465
|
+
END;
|
|
3466
|
+
$$;
|
|
3467
|
+
ALTER FUNCTION public.get_table_ddl_complex(
|
|
3468
|
+
src_schema text,
|
|
3469
|
+
dst_schema text,
|
|
3470
|
+
in_table text,
|
|
3471
|
+
sq_server_version_num integer
|
|
3472
|
+
) OWNER TO postgres;
|
|
3473
|
+
SET default_tablespace = '';
|
|
3474
|
+
SET default_table_access_method = heap;
|
|
3475
|
+
--
|
|
3476
|
+
-- Name: migrations; Type: TABLE; Schema: public; Owner: postgres
|
|
3477
|
+
--
|
|
3478
|
+
|
|
3479
|
+
CREATE TABLE public.migrations (
|
|
3480
|
+
id bigint NOT NULL,
|
|
3481
|
+
name character varying,
|
|
3482
|
+
description text,
|
|
3483
|
+
snapshot jsonb
|
|
3484
|
+
);
|
|
3485
|
+
ALTER TABLE public.migrations OWNER TO postgres;
|
|
3486
|
+
--
|
|
3487
|
+
-- Name: migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
|
|
3488
|
+
--
|
|
3489
|
+
|
|
3490
|
+
CREATE SEQUENCE public.migrations_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
|
3491
|
+
ALTER TABLE public.migrations_id_seq OWNER TO postgres;
|
|
3492
|
+
--
|
|
3493
|
+
-- Name: migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
|
|
3494
|
+
--
|
|
3495
|
+
|
|
3496
|
+
ALTER SEQUENCE public.migrations_id_seq OWNED BY public.migrations.id;
|
|
3497
|
+
--
|
|
3498
|
+
-- Name: migrations id; Type: DEFAULT; Schema: public; Owner: postgres
|
|
3499
|
+
--
|
|
3500
|
+
|
|
3501
|
+
ALTER TABLE ONLY public.migrations
|
|
3502
|
+
ALTER COLUMN id
|
|
3503
|
+
SET DEFAULT nextval('public.migrations_id_seq'::regclass);
|
|
3504
|
+
SELECT pg_catalog.setval('public.migrations_id_seq', 1, false);
|
|
3505
|
+
-- 1. Créer la fonction d'event trigger corrigée
|
|
3506
|
+
CREATE OR REPLACE FUNCTION public.notify_schema_change() RETURNS event_trigger AS $$ BEGIN PERFORM pg_notify('schema_changes', 'Test notification');
|
|
3507
|
+
END;
|
|
3508
|
+
$$ LANGUAGE plpgsql;
|
|
3509
|
+
-- 2. Créer l'event trigger
|
|
3510
|
+
CREATE EVENT TRIGGER schema_change_trigger ON ddl_command_end
|
|
3511
|
+
WHEN TAG IN (
|
|
3512
|
+
'CREATE TABLE',
|
|
3513
|
+
'ALTER TABLE',
|
|
3514
|
+
'DROP TABLE',
|
|
3515
|
+
'CREATE VIEW',
|
|
3516
|
+
'ALTER VIEW',
|
|
3517
|
+
'DROP VIEW',
|
|
3518
|
+
'CREATE INDEX',
|
|
3519
|
+
'DROP INDEX',
|
|
3520
|
+
'CREATE FUNCTION',
|
|
3521
|
+
'ALTER FUNCTION',
|
|
3522
|
+
'DROP FUNCTION'
|
|
3523
|
+
) EXECUTE FUNCTION public.notify_schema_change();
|
|
3524
|
+
|
|
3525
|
+
--
|
|
3526
|
+
-- AUTH Schema
|
|
3527
|
+
--
|
|
3528
|
+
|
|
3529
|
+
CREATE TABLE auth.profil (
|
|
3530
|
+
id bigint NOT NULL,
|
|
3531
|
+
utilisateur_id bigint NOT NULL,
|
|
3532
|
+
role_id integer
|
|
3533
|
+
);
|
|
3534
|
+
CREATE SEQUENCE auth.profil_id_seq
|
|
3535
|
+
START WITH 1
|
|
3536
|
+
INCREMENT BY 1
|
|
3537
|
+
NO MINVALUE
|
|
3538
|
+
NO MAXVALUE
|
|
3539
|
+
CACHE 1;
|
|
3540
|
+
CREATE TABLE auth.roles (
|
|
3541
|
+
id bigint NOT NULL,
|
|
3542
|
+
name character varying(50) NOT NULL
|
|
3543
|
+
);
|
|
3544
|
+
CREATE SEQUENCE auth.roles_id_seq
|
|
3545
|
+
START WITH 1
|
|
3546
|
+
INCREMENT BY 1
|
|
3547
|
+
NO MINVALUE
|
|
3548
|
+
NO MAXVALUE
|
|
3549
|
+
CACHE 1;
|
|
3550
|
+
CREATE TABLE auth.users (
|
|
3551
|
+
id bigint NOT NULL,
|
|
3552
|
+
email character varying(255) NOT NULL,
|
|
3553
|
+
orion_id UUID,
|
|
3554
|
+
info JSONB
|
|
3555
|
+
);
|
|
3556
|
+
CREATE SEQUENCE auth.users_id_seq
|
|
3557
|
+
START WITH 1
|
|
3558
|
+
INCREMENT BY 1
|
|
3559
|
+
NO MINVALUE
|
|
3560
|
+
NO MAXVALUE
|
|
3561
|
+
CACHE 1;
|
|
3562
|
+
|
|
3563
|
+
ALTER TABLE ONLY auth.profil ALTER COLUMN id SET DEFAULT nextval('auth.profil_id_seq'::regclass);
|
|
3564
|
+
ALTER TABLE ONLY auth.roles ALTER COLUMN id SET DEFAULT nextval('auth.roles_id_seq'::regclass);
|
|
3565
|
+
ALTER TABLE ONLY auth.users ALTER COLUMN id SET DEFAULT nextval('auth.users_id_seq'::regclass);
|
|
3566
|
+
|
|
3567
|
+
ALTER TABLE ONLY auth.profil
|
|
3568
|
+
ADD CONSTRAINT profil_pkey PRIMARY KEY (id);
|
|
3569
|
+
ALTER TABLE ONLY auth.roles
|
|
3570
|
+
ADD CONSTRAINT roles_pkey PRIMARY KEY (id);
|
|
3571
|
+
ALTER TABLE ONLY auth.users
|
|
3572
|
+
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
|
3573
|
+
ALTER TABLE ONLY auth.profil
|
|
3574
|
+
ADD CONSTRAINT profil_role_id_fkey FOREIGN KEY (role_id) REFERENCES auth.roles(id);
|
|
3575
|
+
ALTER TABLE ONLY auth.profil
|
|
3576
|
+
ADD CONSTRAINT profil_utilisateurid_fkey FOREIGN KEY (utilisateur_id) REFERENCES auth.users(id);
|
|
3577
|
+
|
|
3578
|
+
ALTER TABLE auth.users
|
|
3579
|
+
ADD CONSTRAINT users_orion_id_unique UNIQUE (orion_id);
|
|
3580
|
+
|
|
3581
|
+
CREATE FUNCTION auth.uid() RETURNS uuid AS $$
|
|
3582
|
+
BEGIN
|
|
3583
|
+
RETURN current_setting('app.current_user_id', true)::uuid;
|
|
3584
|
+
EXCEPTION
|
|
3585
|
+
WHEN OTHERS THEN
|
|
3586
|
+
RETURN NULL;
|
|
3587
|
+
END;
|
|
3588
|
+
$$ LANGUAGE plpgsql;
|
|
3589
|
+
|
|
3590
|
+
|
|
3591
|
+
|
|
3592
|
+
SET statement_timeout = 0;
|
|
3593
|
+
SET lock_timeout = 0;
|
|
3594
|
+
SET idle_in_transaction_session_timeout = 0;
|
|
3595
|
+
SET client_encoding = 'UTF8';
|
|
3596
|
+
SET standard_conforming_strings = on;
|
|
3597
|
+
SELECT pg_catalog.set_config('search_path', '', false);
|
|
3598
|
+
SET check_function_bodies = false;
|
|
3599
|
+
SET xmloption = content;
|
|
3600
|
+
SET client_min_messages = warning;
|
|
3601
|
+
SET row_security = off;
|
|
3602
|
+
--
|
|
3603
|
+
-- PostgreSQL database dump complete
|
|
3604
|
+
--
|