Compare commits

..
46 Commits
Author SHA1 Message Date
Akshay Joshi 002fdb33fc Update version for release. 2020-03-30 17:39:33 +05:30
Pradip Parkale 13b65c9a6a Fixed tab key navigation issue for Grant Wizard. Fixes #5241
1) Added entry for .scss to change the shadow of  'ajs-close' button.
  2) allow the user to use the keyboard to update the Backgrid cell DateTime picker control.
2020-03-30 17:31:50 +05:30
Libor M e6118a06e8 Update Czech translation. 2020-03-29 18:31:51 +05:30
Domenico Sgarbossa 63ac71190b Update Italian translation. 2020-03-29 18:29:03 +05:30
Identifier Anonymous 37542ae706 Update Japanese translation. 2020-03-29 18:25:51 +05:30
Fred Escallier 89d4d835ba Update French translation. 2020-03-29 18:23:17 +05:30
Akshay Joshi 9f0ea70945 Update message catalogs. 2020-03-27 19:02:29 +05:30
Akshay Joshi 86e4a88201 Added 'oid-2' to the ignore list of schema diff for all supported nodes. Fixes #5297 2020-03-27 15:27:48 +05:30
Akshay Joshi c002881f66 Added conkey and indkey to the ignore list for schema diff. 2020-03-27 14:41:18 +05:30
Nagesh Dhope f17d4530a3 Fixed focus color issue for Alertify dialog buttons. Fixes #5292 2020-03-26 15:30:31 +05:30
Akshay Joshi 94a76cc9e0 Added support of Domain, Domain Constraints and Types to the Schema Diff. Fixes #5262 2020-03-26 14:30:09 +05:30
Ganesh Jaybhay 4c0b229b14 Fixed an issue where focus doesn't go to the immediate next editable cell after pressing the tab key on switch cell. 2020-03-26 10:50:01 +05:30
Dave Page 17d0d5e0a2 Use NodeJS v12. 2020-03-24 11:37:32 +00:00
Dave Page f97d29243d Don't offer to configure Apache on Redhat platforms as there's nothing to do. 2020-03-24 11:36:54 +00:00
Libor M 94864104e4 Added and fixed gettext usage for better translation coverage. Fixes #5284
1) Fixed usage gettext('') instead of _('') in javascript files.
  2) Fixed usage gettext('') instead of `${gettext('')}` in javascript files,
     because "pybabel extract" not support extracting from this syntax.
2020-03-24 11:14:05 +05:30
Aditya Toshniwal 6988ce96d7 Change some colors and opacity to comply with WCAG color contrast standards. Fixes #5128 2020-03-23 18:15:39 +05:30
Akshay Joshi 98492e7718 Modified RESQL test cases for Foreign Tables to fix the build failures. 2020-03-22 16:26:23 +05:30
Dave Page becb453e7d Use the correct architecture for web packages on Redhat. 2020-03-20 15:48:00 +00:00
Dave Page 35644fdd35 Update supported distro lists. 2020-03-20 14:51:19 +00:00
Dave Page 5ab056759b Use the proper naming convention for .DEB packages 2020-03-20 12:26:10 +00:00
Dave Page e1aeaba942 Simplify PYTHONPATH handling for Sphinx. 2020-03-20 12:07:35 +00:00
Dave Page e256fc32d9 Ensure rpm-build is installed. 2020-03-20 11:50:59 +00:00
Murtuza Zabuawala a7ba446703 Fixed Unicode character issue causing error on Python2 environment. Fixes #5279 2020-03-20 15:17:45 +05:30
Akshay Joshi 26d0a3e5cb Fixed following audit vulnerabilities:
https://www.npmjs.com/advisories/1179
2020-03-20 11:30:27 +05:30
Dave Page 7f571fa7d8 Add mod_wsgi to the built package list. 2020-03-19 13:17:22 -04:00
Dave Page cb600549c3 Don't hard code the architecture. 2020-03-19 12:56:39 -04:00
Dave Page e1cb6d92c5 Update supported distros list. 2020-03-19 12:56:39 -04:00
Dave Page 8812a7db69 Add support for building RPMs on CentOS/RHEL 7 2020-03-19 12:56:39 -04:00
Pradip Parkale 96361656e4 Fixed some accessibility issues in the dialogs:
1) Aria-label additions at few missing places.
  2) Label additions with  'sr-only' class where hidden labels required.
  3) Converted label to span to solve accessibility error.

Fixes #4608
2020-03-19 16:08:51 +05:30
Murtuza Zabuawala 83785da1bc Enhance the color of switch control for both light and dark theme. Fixes #5271 2020-03-19 13:59:03 +05:30
Akshay Joshi 4fe69e825e Added support of Collation, FTS Configuration, FTS Dictionary, FTS Parser, and FTS Template to the Schema Diff. Fixes #5261 2020-03-19 13:54:25 +05:30
Dave Page 6105fc861d Don't ask to continue when installing deps. 2020-03-18 13:00:33 +00:00
Dave Page f156696339 Add support for building RHEL/CentOS 8 RPMs. 2020-03-18 08:51:11 -04:00
Dave Page b6c8707a4d Don't barf if the tree is clean. 2020-03-16 12:40:01 +00:00
Akshay Joshi 3031c5055a Fixed following audit vulnerabilities:
https://www.npmjs.com/advisories/1488
  https://www.npmjs.com/advisories/1490
2020-03-16 15:47:12 +05:30
Akshay Joshi 44c0d76541 Improve logic to get the DDL statements as a part of the comparison. Fixes #5221
Fixed 23 issues related to schema diff.
2020-03-15 14:52:19 +05:30
Dave Page 0b101d9efd Update tested distros list. 2020-03-13 12:36:08 +00:00
Dave Page b69995b34d Use fakeroot to set file ownership as dpkg-deb can't do it itself on older distros. 2020-03-13 12:17:10 +00:00
Dave Page ef6c9fbd6d Setup dependencies as required on Ubuntu 16.04 and Debian 9 2020-03-13 11:55:03 +00:00
Dave Page e43f58d11f Ensure file/directory ownership is correct. 2020-03-12 15:35:43 +00:00
Dave Page a684b48fb3 Add experimental scripts for building Debian/Ubuntu packages.
Code is split into pkg/debian for the Debian/Ubuntu specific parts,
and pkg/linux for generic code that can be used on other distros.

Tested on Debian 10 and Ubuntu 18.04 at present.
2020-03-12 15:24:58 +00:00
Murtuza Zabuawala 7cc30e3216 Fixed chrome driver download utility issue for Ubuntu. Fixes #4942 2020-03-12 11:18:07 +05:30
Ganesh Jaybhay 6586be5158 Fixed some accessibility issues:
1) Screen reader to read elements in radio modern control.
2) Aria-label additions at few missing places.
3) Radiocontrol to have different ids for each option.
2020-03-12 11:09:29 +05:30
Pradip Parkale 1bd14895c3 Fix an issue where the user can not change the value of DateTime picker control using keyboard. Fixes #4237. 2020-03-10 13:21:17 +05:30
Pradip Parkale 82155cd859 Fix an accessibility issue to maximize the panel for all alertify dialog. Fixes #5143 2020-03-06 18:47:22 +05:30
Nagesh Dhope 0ed3cd248b Set Active query details subnode control as read-only instead of disabled for accessibility. 2020-03-06 17:59:57 +05:30
194 changed files with 13212 additions and 5848 deletions

No files matched your search

+5 -3
View File
@@ -1,8 +1,6 @@
*.autosave
*.db
*.diff
*.o
*.patch
*.psp
*.pyc
*.pyo
@@ -11,14 +9,18 @@
.DS_Store
.idea
.vscode
/*.diff
/*.patch
/dist
/debian-build
/docker-build
/mac-build
/pip-build
/redhat-build
/src-build
/win-build
_build
build-*
/build-*
pgadmin4.log
pkg/win32/installer.iss
pkg/mac/codesign.conf
+13 -1
View File
@@ -91,7 +91,7 @@ runtime:
cd runtime && qmake CONFIG+=release && make
# Include all clean sub-targets in clean
clean: clean-appbundle clean-dist clean-docs clean-node clean-pip clean-src clean-runtime
clean: clean-appbundle clean-debian clean-dist clean-docs clean-node clean-pip clean-redhat clean-src clean-runtime
rm -rf web/pgadmin/static/js/generated/*
rm -rf web/pgadmin/static/js/generated/.cache
rm -rf web/pgadmin/static/css/generated/*
@@ -104,6 +104,9 @@ clean-runtime:
clean-appbundle:
rm -rf mac-build/
clean-debian:
rm -rf debian-build/
clean-dist:
rm -rf dist/
@@ -116,9 +119,15 @@ clean-node:
clean-pip:
rm -rf pip-build/
clean-redhat:
rm -rf redhat-build/
clean-src:
rm -rf src-build/
debian:
./pkg/debian/build.sh
docker:
echo $(APP_NAME)
docker build -t ${APP_NAME} -t $(APP_NAME):latest -t $(APP_NAME):$(APP_RELEASE) -t $(APP_NAME):$(APP_RELEASE).$(APP_REVISION) .
@@ -148,5 +157,8 @@ msg-update:
pip: docs
./pkg/pip/build.sh
redhat:
./pkg/redhat/build.sh
src:
./pkg/src/build.sh
+1
View File
@@ -11,6 +11,7 @@ notes for it.
.. toctree::
:maxdepth: 1
release_notes_4_20
release_notes_4_19
release_notes_4_18
release_notes_4_17
+32
View File
@@ -0,0 +1,32 @@
************
Version 4.20
************
Release date: 2020-04-02
This release contains a number of bug fixes and new features since the release of pgAdmin4 4.19.
New features
************
| `Issue #5261 <https://redmine.postgresql.org/issues/5261>`_ - Added support of Collation, FTS Configuration, FTS Dictionary, FTS Parser and FTS Template to the Schema Diff.
| `Issue #5262 <https://redmine.postgresql.org/issues/5262>`_ - Added support of Domain, Domain Constraints and Types to the Schema Diff.
Housekeeping
************
| `Issue #5271 <https://redmine.postgresql.org/issues/5271>`_ - Enhance the color of switch control for both light and dark theme.
| `Issue #5284 <https://redmine.postgresql.org/issues/5284>`_ - Added and fixed gettext usage for better translation coverage.
Bug fixes
*********
| `Issue #4237 <https://redmine.postgresql.org/issues/4237>`_ - Fix an issue where the user can not change the value of DateTime picker control using keyboard.
| `Issue #4608 <https://redmine.postgresql.org/issues/4608>`_ - Fixed some accessibility issues in the dialogs.
| `Issue #4942 <https://redmine.postgresql.org/issues/4942>`_ - Fixed chrome driver download utility issue for Ubuntu.
| `Issue #5128 <https://redmine.postgresql.org/issues/5128>`_ - Change some colors and opacity to comply with WCAG color contrast standards.
| `Issue #5143 <https://redmine.postgresql.org/issues/5143>`_ - Fix an accessibility issue to maximize the panel for all alertify dialog.
| `Issue #5221 <https://redmine.postgresql.org/issues/5221>`_ - Improve logic to get the DDL statements as a part of the comparison.
| `Issue #5241 <https://redmine.postgresql.org/issues/5241>`_ - Fixed tab key navigation issue for Grant Wizard.
| `Issue #5279 <https://redmine.postgresql.org/issues/5279>`_ - Fixed Unicode character issue causing error on Python2 environment.
| `Issue #5292 <https://redmine.postgresql.org/issues/5292>`_ - Fixed focus color issue for Alertify dialog buttons.
+47
View File
@@ -0,0 +1,47 @@
This directory contains the build runner script for creating .DEB packages for
Debian and Ubuntu.
Build configuration
===================
To build Debian/Ubuntu packages, first run the setup.sh script as root to
install the required pre-requisites, e.g.
# pkg/debian/setup.sh
Building packages
=================
To build a set of packages, from the top-level source directory run:
$ make debian
or
$ pkg/debian/build.sh
Four .deb packages will be created in the dist/ directory:
pgadmin4_<version>_<distro>_<distro_version>_all.deb
A convenience package that depends on all the others.
pgadmin4-server_<version>_<distro>_<distro_version>_<arch>.deb
The core server, e.g. the Python and JS code and the online documentation.
pgadmin4-desktop_<version>_<distro>_<distro_version>_<arch>.deb
The desktop runtime. Requires the server package.
pgadmin4-web_<version>_<distro>_<distro_version>_<arch>.deb
The server mode setup script for configuring Apache HTTPD. Requires the
server package.
Supported platforms
===================
Debian 9 & 10
Ubuntu 16.04, 18.04, 19.04 & 19.10
Warning
=======
These scripts are experimental - use at your own risk!
+108
View File
@@ -0,0 +1,108 @@
#!/bin/bash
# Exit when any command fails
set -e
# Debugging shizz
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
trap 'if [ $? -ne 0 ]; then echo "\"${last_command}\" command filed with exit code $?."; fi' EXIT
OS_VERSION=$(cat /etc/os-release | grep "^VERSION_ID=" | awk -F "=" '{ print $2 }' | sed 's/"//g')
OS_NAME=$(cat /etc/os-release | grep "^ID=" | awk -F "=" '{ print $2 }' | sed 's/"//g')
OS_ARCH=$(dpkg-architecture -qDEB_HOST_ARCH)
# Common Linux build functions
source pkg/linux/build-functions.sh
# Assemble the "standard" installation footprint
_setup_env $0 "debian"
_cleanup "deb"
_setup_dirs
_create_python_virtualenv "debian"
_build_runtime
_build_docs "debian"
_copy_code
#
# Server package
#
# Create the Debian packaging stuffs for the server
echo "Creating the server package..."
mkdir "${SERVERROOT}/DEBIAN"
cat << EOF > "${SERVERROOT}/DEBIAN/control"
Package: ${APP_NAME}-server
Version: ${APP_LONG_VERSION}
Architecture: ${OS_ARCH}
Depends: python3, libpq5
Recommends: postgresql-client | postgresql-client-12 | postgresql-client-11 | postgresql-client-10
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
Description: The core server package for pgAdmin. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
EOF
# Build the Debian package for the server
fakeroot dpkg-deb --build "${SERVERROOT}" "${DISTROOT}/${APP_NAME}-server_${APP_LONG_VERSION}_${OS_ARCH}.deb"
#
# Desktop package
#
# Create the Debian packaging stuffs for the desktop
echo "Creating the desktop package..."
mkdir "${DESKTOPROOT}/DEBIAN"
cat << EOF > "${DESKTOPROOT}/DEBIAN/control"
Package: ${APP_NAME}-desktop
Version: ${APP_LONG_VERSION}
Architecture: ${OS_ARCH}
Depends: ${APP_NAME}-server, libqt5gui5
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
Description: The desktop user interface for pgAdmin. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
EOF
# Build the Debian package for the server
fakeroot dpkg-deb --build "${DESKTOPROOT}" "${DISTROOT}/${APP_NAME}-desktop_${APP_LONG_VERSION}_${OS_ARCH}.deb"
#
# Web package
#
# Create the Debian packaging stuffs for the web
echo "Creating the web package..."
mkdir "${WEBROOT}/DEBIAN"
cat << EOF > "${WEBROOT}/DEBIAN/control"
Package: ${APP_NAME}-web
Version: ${APP_LONG_VERSION}
Architecture: all
Depends: ${APP_NAME}-server, apache2, libapache2-mod-wsgi-py3
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
Description: The web interface for pgAdmin, hosted under Apache HTTPD. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
EOF
mkdir -p "${WEBROOT}/etc/apache2/conf-available"
cp "${SOURCEDIR}/pkg/debian/pgadmin4.conf" "${WEBROOT}/etc/apache2/conf-available"
# Build the Debian package for the web
fakeroot dpkg-deb --build "${WEBROOT}" "${DISTROOT}/${APP_NAME}-web_${APP_LONG_VERSION}_all.deb"
#
# Meta package
#
# Create the Debian packaging stuffs for the meta package
echo "Creating the meta package..."
mkdir "${METAROOT}/DEBIAN"
cat << EOF > "${METAROOT}/DEBIAN/control"
Package: ${APP_NAME}
Version: ${APP_LONG_VERSION}
Architecture: all
Depends: ${APP_NAME}-server, ${APP_NAME}-desktop, ${APP_NAME}-web
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
Description: Installs all required components to run pgAdmin in desktop and web modes. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
EOF
# Build the Debian meta package
fakeroot dpkg-deb --build "${METAROOT}" "${DISTROOT}/${APP_NAME}_${APP_LONG_VERSION}_all.deb"
+9
View File
@@ -0,0 +1,9 @@
WSGIDaemonProcess pgadmin processes=1 threads=25 python-home=/usr/pgadmin4/venv
WSGIScriptAlias /pgadmin4 /usr/pgadmin4/web/pgAdmin4.wsgi
<Directory /usr/pgadmin4/web/>
WSGIProcessGroup pgadmin
WSGIApplicationGroup %{GLOBAL}
Require all granted
</Directory>
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
if [ "$EUID" -ne 0 ]
then echo "This script must be run as root"
exit 1
fi
echo "Installing system pre-requisites..."
apt install -y curl apt-transport-https ca-certificates
echo "Removing yarn (which may not be the package we expect)..."
dpkg -r yarn
# Node repo
echo "Setting up the NodeJS repo..."
curl -sL https://deb.nodesource.com/setup_12.x | bash -
# Yarn repo
echo "Setting up the Yarn repo..."
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
echo "Running apt update..."
apt update
# Install pre-reqs
echo "Installing build pre-requisites..."
apt install -y build-essential python3-dev python3-venv python3-sphinx libpq-dev libffi-dev qt5-default nodejs yarn
+3
View File
@@ -0,0 +1,3 @@
This directory contains package build scripts and assets that are common to various Linux distributions.
You should not use them directly!
+161
View File
@@ -0,0 +1,161 @@
_setup_env() {
echo "Setting up the environment..."
WD=$(cd `dirname "$1"` && pwd)
SOURCEDIR=$(realpath "${WD}/../..")
BUILDROOT=$(realpath "${WD}/../../$2-build")
DESKTOPROOT=${BUILDROOT}/desktop
METAROOT=${BUILDROOT}/meta
SERVERROOT=${BUILDROOT}/server
WEBROOT=${BUILDROOT}/web
DISTROOT=$(realpath "${WD}/../../dist")
APP_RELEASE=`grep "^APP_RELEASE" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
APP_REVISION=`grep "^APP_REVISION" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
APP_NAME=`grep "^APP_NAME" web/config.py | cut -d"=" -f2 | sed "s/'//g" | sed 's/^ //' | sed 's/ //g' | tr '[:upper:]' '[:lower:]'`
APP_LONG_VERSION=${APP_RELEASE}.${APP_REVISION}
APP_SHORT_VERSION=`echo ${APP_LONG_VERSION} | cut -d . -f1,2`
APP_SUFFIX=`grep "^APP_SUFFIX" web/config.py | cut -d"=" -f2 | sed 's/ //g' | sed "s/'//g"`
if [ ! -z ${APP_SUFFIX} ]; then
APP_LONG_VERSION=${APP_LONG_VERSION}-${APP_SUFFIX}
fi
}
_cleanup() {
echo "Cleaning up the old environment and app..."
if test -f ${SOURCEDIR}/runtime/pgAdmin4; then
rm -rf ${SOURCEDIR}/runtime/pgAdmin4
fi
if test -d ${BUILDROOT}; then
rm -rf ${BUILDROOT}
fi
rm -f ${DISTROOT}/pgadmin4*.$1
}
_setup_dirs() {
echo "Creating output directories..."
test -d ${BUILDROOT} || mkdir ${BUILDROOT}
test -d ${DESKTOPROOT} || mkdir ${DESKTOPROOT}
test -d ${METAROOT} || mkdir ${METAROOT}
test -d ${SERVERROOT} || mkdir ${SERVERROOT}
test -d ${WEBROOT} || mkdir ${WEBROOT}
test -d ${DISTROOT} || mkdir ${DISTROOT}
}
_create_python_virtualenv() {
echo "Creating the virtual environment..."
cd ${SERVERROOT}
# Create the required directories
mkdir -p "usr/${APP_NAME}"
cd "usr/${APP_NAME}"
# Create the blank venv
python3 -m venv venv
source venv/bin/activate
pip3 install --no-cache-dir --no-binary psycopg2 -r ${SOURCEDIR}/requirements.txt
# Figure out some paths for use when completing the venv
# Use "python3" here as we want the venv path
PYMODULES_PATH=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
DIR_PYMODULES_PATH=`dirname ${PYMODULES_PATH}`
# Use /usr/bin/python3 here as we want the system path
if [ $1 == "debian" ]; then
PYSYSLIB_PATH=$(/usr/bin/python3 -c "import sys; print('%s/lib/python%d.%.d' % (sys.prefix, sys.version_info.major, sys.version_info.minor))")
else
PYSYSLIB_PATH=$(/usr/bin/python3 -c "import sys; print('%s/lib64/python%d.%.d' % (sys.prefix, sys.version_info.major, sys.version_info.minor))")
fi
# Symlink in the rest of the Python libs. This is required because the runtime
# will clear PYTHONHOME for safety, which has the side-effect of preventing
# it from finding modules that are not explicitly included in the venv
cd ${DIR_PYMODULES_PATH}
# Files
for FULLPATH in ${PYSYSLIB_PATH}/*.py; do
FILE=${FULLPATH##*/}
if [ ! -e ${FILE} ]; then
ln -s ${FULLPATH} ${FILE}
fi
done
# Paths
for FULLPATH in ${PYSYSLIB_PATH}/*/; do
FULLPATH=${FULLPATH%*/}
FILE=${FULLPATH##*/}
if [ ! -e ${FILE} ]; then
ln -s ${FULLPATH} ${FILE}
fi
done
# Remove tests
cd site-packages
find . -name "test" -type d -print0 | xargs -0 rm -rf
find . -name "tests" -type d -print0 | xargs -0 rm -rf
# Link the python<version> directory to python so that the private environment path is found by the application.
if test -d ${DIR_PYMODULES_PATH}; then
ln -s $(basename ${DIR_PYMODULES_PATH}) ${DIR_PYMODULES_PATH}/../python
fi
}
_build_runtime() {
echo "Building the desktop runtime..."
cd ${SOURCEDIR}/runtime
if [ -f Makefile ]; then
make clean
fi
if hash qmake-qt5 2>/dev/null; then
qmake-qt5
else
qmake
fi
make
mkdir -p "${DESKTOPROOT}/usr/${APP_NAME}/bin"
cp pgAdmin4 "${DESKTOPROOT}/usr/${APP_NAME}/bin/pgadmin4"
mkdir -p "${DESKTOPROOT}/usr/${APP_NAME}/share"
cp pgAdmin4.ico "${DESKTOPROOT}/usr/${APP_NAME}/share/pgadmin4.ico"
mkdir -p "${DESKTOPROOT}/usr/share/applications"
cp ../pkg/linux/pgadmin4.desktop "${DESKTOPROOT}/usr/share/applications"
}
_build_docs() {
echo "Building the documentation..."
cd "${SERVERROOT}" && mkdir -p "usr/${APP_NAME}/share/docs/en_US/html"
cd "${SOURCEDIR}/docs/en_US"
python3 build_code_snippet.py
SYS_PYTHONPATH=$(/usr/bin/python3 -c "import sys; print(':'.join([p for p in sys.path if p]))")
if [ $1 == "redhat" -a "${OS_VERSION}" == "7" ]; then
PYTHONPATH=$PYTHONPATH:${SYS_PYTHONPATH} python3 /usr/local/bin/sphinx-build . "${SERVERROOT}/usr/${APP_NAME}/share/docs/en_US/html"
else
PYTHONPATH=$PYTHONPATH:${SYS_PYTHONPATH} python3 -msphinx . "${SERVERROOT}/usr/${APP_NAME}/share/docs/en_US/html"
fi
}
_copy_code() {
echo "Copying the server code..."
# Remove any TCL-related files that may cause us problems
find "${SERVERROOT}/usr/${APP_NAME}/venv/" -name "_tkinter*" -print0 | xargs -0 rm -rf
pushd ${SOURCEDIR}/web
yarn install
yarn run bundle
popd
# copy the web directory to the bundle as it is required by runtime
cp -r "${SOURCEDIR}/web" "${SERVERROOT}/usr/${APP_NAME}/web/"
cp "${SOURCEDIR}/pkg/linux/config_distro.py" "${SERVERROOT}/usr/${APP_NAME}/web/"
cd "${SERVERROOT}/usr/${APP_NAME}/web/"
rm -f pgadmin4.db config_local.*
rm -rf karma.conf.js package.json node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
find . -name "tests" -type d -print0 | xargs -0 rm -rf
find . -name "feature_tests" -type d -print0 | xargs -0 rm -rf
find . -name "__pycache__" -type d -print0 | xargs -0 rm -rf
# Web setup script
mkdir -p "${WEBROOT}/usr/${APP_NAME}/bin/"
cp "${SOURCEDIR}/pkg/linux/setup-web.sh" "${WEBROOT}/usr/${APP_NAME}/bin/"
}
+5
View File
@@ -0,0 +1,5 @@
HELP_PATH = '../../../share/docs/en_US/html/'
DEFAULT_BINARY_PATHS = {
'pg': '/usr/bin',
'ppas': ''
}
+10
View File
@@ -0,0 +1,10 @@
[Desktop Entry]
Encoding=UTF-8
Name=pgAdmin 4
Exec=/usr/pgadmin4/bin/pgadmin4
Icon=/usr/pgadmin4/share/pgadmin4.ico
Type=Application
Categories=Application;Development;
MimeType=text/html
Comment=Management tools for PostgreSQL
Keywords=database;db;sql;query;administration;development;
+107
View File
@@ -0,0 +1,107 @@
#!/bin/bash
#
# Setup pgadmin4 in server mode
#
if [ "$EUID" -ne 0 ]
then echo "This script must be run as root"
exit 1
fi
# Get the distro
IS_REDHAT=0
IS_DEBIAN=0
UNAME=$(uname -a)
if [ -f /etc/redhat-release ]; then
IS_REDHAT=1
APACHE=httpd
echo "Setting up pgAdmin 4 in web mode on a Redhat platform..."
elif [[ ${UNAME} =~ "Ubuntu" ]] || [[ ${UNAME} =~ "debian" ]]; then
IS_DEBIAN=1
APACHE=apache2
echo "Setting up pgAdmin 4 in web mode on a Debian platform..."
fi
# Run setup script first:
echo "Creating configuration database..."
/usr/pgadmin4/venv/bin/python3 /usr/pgadmin4/web/setup.py
if [ $? != 0 ]
then
echo "Error setting up server mode. Please examine the output above."
exit 1
fi
# Create and own directories:
echo "Creating storage and log directories..."
mkdir -p /var/log/pgadmin /var/lib/pgadmin
if [ ${IS_REDHAT} == 1 ]; then
chown apache: /var/log/pgadmin /var/lib/pgadmin -R
else
chown www-data: /var/log/pgadmin /var/lib/pgadmin -R
fi
# Set SELinux up:
if [ ${IS_REDHAT} == 1 ]; then
echo "Configuring SELinux..."
setsebool -P httpd_can_network_connect 1
setsebool -P httpd_can_network_connect_db 1
semanage fcontext -a -t httpd_var_lib_t '/var/lib/pgadmin(/.*)?'
restorecon -R -v /var/lib/pgadmin
semanage fcontext -a -t httpd_log_t '/var/log/pgadmin(/.*)?'
restorecon -R -v /var/log/pgadmin
fi
# Setup Apache on Debian/Ubuntu
if [ ${IS_DEBIAN} == 1 ]; then
read -p "We can now configure the Apache Web server for you. This involves enabling the wsgi module and configuring the pgAdmin 4 application to mount at /pgadmin4. Do you wish to continue (y/n)? " RESPONSE
case ${RESPONSE} in
y|Y )
a2enmod wsgi 1> /dev/null
a2enconf pgadmin4 1> /dev/null
;;
* )
exit 1;;
esac
fi
APACHE_STATUS=`ps cax | grep ${APACHE}`
if [ $? -eq 0 ]; then
read -p "The Apache web server is running. A restart is required for the pgAdmin 4 installation to complete. Would you like to continue (y/n)? " RESPONSE
case ${RESPONSE} in
y|Y )
systemctl restart ${APACHE}
if [ $? != 0 ]; then
echo "Error restarting ${APACHE}. Please check the systemd logs"
else
echo "Apache successfully restarted. You can now start using pgAdmin 4 in web mode"
fi;;
* )
exit 1;;
esac
else
read -p "The Apache web server is not running. We can enable and start the web server for you to finish pgAdmin 4 installation. Would you like to continue (y/n)? " RESPONSE
case ${RESPONSE} in
y|Y )
systemctl enable ${APACHE}
if [ $? != 0 ]; then
echo "Error enabling ${APACHE}. Please check the systemd logs"
else
echo "Apache successfully enabled."
fi
systemctl start ${APACHE}
if [ $? != 0 ]; then
echo "Error starting ${APACHE}. Please check the systemd logs"
else
echo "Apache successfully started. You can now start using pgAdmin 4 in web mode"
fi;;
* )
exit 1;;
esac
fi
exit 0
+51
View File
@@ -0,0 +1,51 @@
This directory contains the build runner script for creating .RPM packages for
Redhat distributions.
Build configuration
===================
To build RPM packages, first run the setup.sh script as root to install the
required pre-requisites, e.g.
# pkg/redhat/setup.sh
Building packages
=================
To build a set of packages, from the top-level source directory run:
$ make redhat
or
$ pkg/redhat/build.sh
Four .rpm packages will be created in the dist/ directory:
pgadmin4-<version>.<distro>_noarch.rpm
A convenience package that depends on all the others.
pgadmin4-server-<version>.<distro>.<arch>.rpm
The core server, e.g. the Python and JS code and the online documentation.
pgadmin4-desktop-<version>.<distro>.<arch>.rpm
The desktop runtime. Requires the server package.
pgadmin4-web-<version>.<distro>.<arch>.rpm
The server mode setup script for configuring Apache HTTPD. Requires the
server package.
pgadmin4-python3-mod_wsgi-4.7.1-2.el7.<arch>.rpm
The Python 3 build of mod_wsgi for the Apache HTTPD server. Only built
on RHEL/CentOS 7.
Supported platforms
===================
Fedora 30 & 31
RHEL/CentOS 7 & 8
Warning
=======
These scripts are experimental - use at your own risk!
+214
View File
@@ -0,0 +1,214 @@
#!/bin/bash
# Exit when any command fails
set -e
# Debugging shizz
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
trap 'if [ $? -ne 0 ]; then echo "\"${last_command}\" command filed with exit code $?."; fi' EXIT
OS_VERSION=$(cat /etc/os-release | grep "^VERSION_ID=" | awk -F "=" '{ print $2 }' | sed 's/"//g')
OS_NAME=$(cat /etc/os-release | grep "^ID=" | awk -F "=" '{ print $2 }' | sed 's/"//g')
OS_ARCH=$(arch)
# Common Linux build functions
source pkg/linux/build-functions.sh
# Assemble the "standard" installation footprint
_setup_env $0 "redhat"
_cleanup "rpm"
_setup_dirs
_create_python_virtualenv "redhat"
_build_runtime
_build_docs "redhat"
_copy_code
#
# Server package
#
# Create the Redhat packaging stuffs for the server
echo "Creating the server package..."
cat << EOF > "${BUILDROOT}/server.spec"
%global __requires_exclude_from ^/.*$
%global __provides_exclude_from ^/.*$
# Bytecompiling Python 3 doesn't work on RHEL/CentOS 7, so make it a no-op
%if 0%{?rhel} && 0%{?rhel} == 7
%define __python /bin/true
%endif
%undefine __brp_mangle_shebangs
%undefine __brp_ldconfig
Name: ${APP_NAME}-server
Version: ${APP_LONG_VERSION}
Release: 1%{?dist}
Summary: The core server package for pgAdmin.
License: PostgreSQL
URL: https://www.pgadmin.org/
%if 0%{?rhel} && 0%{?rhel} != 7
Requires: python3, libpq
Recommends: postgresql
%else
Requires: python3, postgresql
%endif
%description
The core server package for pgAdmin. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
%build
%install
cp -rfa %{pga_build_root}/server/* \${RPM_BUILD_ROOT}
%files
/usr/pgadmin4/*
EOF
# Build the Redhat package for the server
rpmbuild --define "pga_build_root ${BUILDROOT}" -bb "${BUILDROOT}/server.spec"
#
# Desktop package
#
# Create the Redhat packaging stuffs for the desktop
echo "Creating the desktop package..."
cat << EOF > "${BUILDROOT}/desktop.spec"
%global __requires_exclude_from ^/.*$
%global __provides_exclude_from ^/.*$
%undefine __brp_mangle_shebangs
%undefine __brp_ldconfig
Name: ${APP_NAME}-desktop
Version: ${APP_LONG_VERSION}
Release: 1%{?dist}
Summary: The desktop user interface for pgAdmin.
License: PostgreSQL
URL: https://www.pgadmin.org/
Requires: ${APP_NAME}-server, qt5-qtbase, qt5-qtbase-gui
%description
The desktop user interface for pgAdmin. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
%build
%install
cp -rfa %{pga_build_root}/desktop/* \${RPM_BUILD_ROOT}
%files
/usr/pgadmin4/bin/*
/usr/pgadmin4/share/*
/usr/share/applications/*
EOF
# Build the Redhat package for the server
rpmbuild --define "pga_build_root ${BUILDROOT}" -bb "${BUILDROOT}/desktop.spec"
#
# Web package
#
# Create the Redhat packaging stuffs for the web
echo "Creating the web package..."
cat << EOF > "${BUILDROOT}/web.spec"
%global __requires_exclude_from ^/.*$
%global __provides_exclude_from ^/.*$
%undefine __brp_mangle_shebangs
%undefine __brp_ldconfig
Name: ${APP_NAME}-web
Version: ${APP_LONG_VERSION}
Release: 1%{?dist}
BuildArch: noarch
Summary: The web interface for pgAdmin, hosted under Apache HTTPD.
License: PostgreSQL
URL: https://www.pgadmin.org/
%if 0%{?rhel} && 0%{?rhel} == 7
Requires: ${APP_NAME}-server, httpd, pgadmin4-python3-mod_wsgi
%else
Requires: ${APP_NAME}-server, httpd, python3-mod_wsgi
%endif
%description
The web interface for pgAdmin, hosted under Apache HTTPD. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
%build
%install
cp -rfa %{pga_build_root}/web/* \${RPM_BUILD_ROOT}
%files
/usr/pgadmin4/bin/*
/etc/httpd/conf.d/*
EOF
mkdir -p "${WEBROOT}/etc/httpd/conf.d"
cp "${SOURCEDIR}/pkg/redhat/pgadmin4.conf" "${WEBROOT}/etc/httpd/conf.d"
# Build the Redhat package for the web
rpmbuild --define "pga_build_root ${BUILDROOT}" -bb "${BUILDROOT}/web.spec"
#
# Meta package
#
# Create the Redhat packaging stuffs for the meta package
echo "Creating the meta package..."
cat << EOF > "${BUILDROOT}/meta.spec"
%global __requires_exclude_from ^/.*$
%global __provides_exclude_from ^/.*$
%undefine __brp_mangle_shebangs
%undefine __brp_ldconfig
Name: ${APP_NAME}
Version: ${APP_LONG_VERSION}
Release: 1%{?dist}
BuildArch: noarch
Summary: Installs all required components to run pgAdmin in desktop and web modes.
License: PostgreSQL
URL: https://www.pgadmin.org/
Requires: ${APP_NAME}-server, ${APP_NAME}-desktop, ${APP_NAME}-web
%description
Installs all required components to run pgAdmin in desktop and web modes. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
%build
%install
%files
EOF
# Build the Redhat package for the meta package
rpmbuild --define "pga_build_root ${BUILDROOT}" -bb "${BUILDROOT}/meta.spec"
#
# mod_wsgi for CentOS 7
#
if [ ${OS_VERSION} == 7 ]; then
cp "${SOURCEDIR}/pkg/redhat/pgadmin4-python3-mod_wsgi-exports.patch" ${HOME}/rpmbuild/SOURCES
cp "${SOURCEDIR}/pkg/redhat/pgadmin4-python3-mod_wsgi.conf" ${HOME}/rpmbuild/SOURCES
curl -o ${HOME}/rpmbuild/SOURCES/mod_wsgi-4.7.1.tar.gz https://codeload.github.com/GrahamDumpleton/mod_wsgi/tar.gz/4.7.1
rpmbuild -bb "${SOURCEDIR}/pkg/redhat/pgadmin4-python-mod_wsgi.spec"
fi
#
# Get the results!
#
cp ${HOME}/rpmbuild/RPMS/${OS_ARCH}/${APP_NAME}-*${APP_LONG_VERSION}-*.${OS_ARCH}.rpm "${DISTROOT}/"
cp ${HOME}/rpmbuild/RPMS/noarch/${APP_NAME}-*${APP_LONG_VERSION}-*.noarch.rpm "${DISTROOT}/"
if [ ${OS_VERSION} == 7 ]; then
cp ${HOME}/rpmbuild/RPMS/${OS_ARCH}/pgadmin4-python3-mod_wsgi-4.7.1-2.el7.x86_64.rpm "${DISTROOT}/"
fi
+71
View File
@@ -0,0 +1,71 @@
%global sname mod_wsgi
%{!?_httpd_apxs: %{expand: %%global _httpd_apxs %%{_sbindir}/apxs}}
%{!?_httpd_mmn: %{expand: %%global _httpd_mmn %%(cat %{_includedir}/httpd/.mmn 2>/dev/null || echo 0-0)}}
%{!?_httpd_confdir: %{expand: %%global _httpd_confdir %%{_sysconfdir}/httpd/conf.d}}
# /etc/httpd/conf.d with httpd < 2.4 and defined as /etc/httpd/conf.modules.d with httpd >= 2.4
%{!?_httpd_modconfdir: %{expand: %%global _httpd_modconfdir %%{_sysconfdir}/httpd/conf.d}}
%{!?_httpd_moddir: %{expand: %%global _httpd_moddir %%{_libdir}/httpd/modules}}
%global debug_package %{nil}
Name: pgadmin4-python3-%{sname}
Version: 4.7.1
Release: 2%{?dist}
Summary: A WSGI interface for Python web applications in Apache (customized for pgAdmin4)
License: ASL 2.0
URL: https://modwsgi.readthedocs.io/
Source0: https://github.com/GrahamDumpleton/%{sname}/archive/%{version}.tar.gz#/mod_wsgi-%{version}.tar.gz
Source2: %{name}.conf
Patch1: %{name}-exports.patch
Requires: httpd-mmn = %{_httpd_mmn}
BuildRequires: python3-devel
BuildRequires: httpd-devel
BuildRequires: gcc
# Suppress auto-provides for module DSO
%{?filter_provides_in: %filter_provides_in %{_httpd_moddir}/.*\.so$}
%{?filter_setup}
%global _description\
The mod_wsgi adapter is an Apache module that provides a WSGI compliant\
interface for hosting Python based web applications within Apache. The\
adapter is written completely in C code against the Apache C runtime and\
for hosting WSGI applications within Apache has a lower overhead than using\
existing WSGI adapters for mod_python or CGI.\
%description %_description
%prep
%autosetup -p1 -n %{sname}-%{version}
%build
export LDFLAGS="$RPM_LD_FLAGS -L%{_libdir}"
export CFLAGS="$RPM_OPT_FLAGS -fno-strict-aliasing"
%configure --enable-shared --with-apxs=%{_httpd_apxs} --with-python=python3
%{__make} %{?_smp_mflags}
%{_bindir}/python3 setup.py build
%install
%{__make} install DESTDIR=%{buildroot} LIBEXECDIR=%{_httpd_moddir}
%{__install} -d -m 755 %{buildroot}%{_httpd_modconfdir}
%{__install} -p -m 644 %{SOURCE2} %{buildroot}%{_httpd_modconfdir}/10-pgadmin4-python3-mod_wsgi.conf
%{_bindir}/python3 setup.py install -O1 --skip-build --root %{buildroot}
%{__mv} %{buildroot}%{_httpd_moddir}/mod_wsgi.so %{buildroot}%{_httpd_moddir}/pgadmin4-python3-mod_wsgi.so
%{__mv} %{buildroot}%{_bindir}/mod_wsgi-express %{buildroot}%{_bindir}/pgadmin4-mod_wsgi-express-3
%files
%license LICENSE
%doc CREDITS.rst README.rst
%config(noreplace) %{_httpd_modconfdir}/*pgadmin4-python3-mod_wsgi.conf
%{_httpd_moddir}/pgadmin4-python3-mod_wsgi.so
%{python3_sitearch}/mod_wsgi-*.egg-info
%{python3_sitearch}/mod_wsgi
%{_bindir}/pgadmin4-mod_wsgi-express-3
%changelog
* Fri Mar 6 2020 Devrim Gündüz <devrim@gunduz.org> - 4.6.8-2
- Initial packaging for the PostgreSQL YUM repository
@@ -0,0 +1,11 @@
--- mod_wsgi-4.5.20/Makefile.in.exports
+++ mod_wsgi-4.5.20/Makefile.in
@@ -28,7 +28,7 @@
all : src/server/mod_wsgi.la
src/server/mod_wsgi.la : $(SRCFILES)
- $(APXS) -c $(CPPFLAGS) $(CFLAGS) $(SRCFILES) $(LDFLAGS) $(LDLIBS)
+ $(APXS) -Wl,-export-symbols-regex -Wl,wsgi_module -c $(CPPFLAGS) $(CFLAGS) $(SRCFILES) $(LDFLAGS) $(LDLIBS)
$(DESTDIR)$(LIBEXECDIR) :
mkdir -p $@
@@ -0,0 +1,4 @@
<IfModule !wsgi_module>
LoadModule wsgi_module modules/pgadmin4-python3-mod_wsgi.so
</IfModule>
+19
View File
@@ -0,0 +1,19 @@
LoadModule wsgi_module modules/mod_wsgi.so
WSGIDaemonProcess pgadmin processes=1 threads=25 python-home=/usr/pgadmin4/venv
WSGIScriptAlias /pgadmin4 /usr/pgadmin4/web/pgAdmin4.wsgi
<Directory /usr/pgadmin4/web/>
WSGIProcessGroup pgadmin
WSGIApplicationGroup %{GLOBAL}
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order Deny,Allow
Deny from All
Allow from 127.0.0.1
Allow from ::1
</IfModule>
</Directory>
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
if [ "$EUID" -ne 0 ]
then echo "This script must be run as root"
exit 1
fi
OS_VERSION=$(cat /etc/os-release | grep "^VERSION_ID=" | awk -F "=" '{ print $2 }' | sed 's/"//g')
# EPEL & other repos
yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-${OS_VERSION}.noarch.rpm
yum config-manager --enable PowerTools AppStream BaseOS *epel
# Node repo
echo "Setting up the NodeJS repo..."
curl -sL https://rpm.nodesource.com/setup_12.x | bash -
# Yarn repo
echo "Setting up the Yarn repo..."
curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
# Install pre-reqs
echo "Installing build pre-requisites..."
yum groupinstall -y "Development Tools"
if [ ${OS_VERSION} == 7 ]; then
yum install -y fakeroot httpd-devel qt5-qtbase-devel postgresql-devel python3-devel nodejs yarn rpm-build
pip3 install sphinx
else
yum install -y fakeroot qt5-qtbase-devel libpq-devel python3-devel python3-sphinx nodejs yarn rpm-build
fi
+2 -2
View File
@@ -8,9 +8,9 @@
<key>CFBundleName</key>
<string>pgAdmin 4</string>
<key>CFBundleShortVersionString</key>
<string>4.19.0</string>
<string>4.20.0</string>
<key>CFBundleVersion</key>
<string>4.19.0</string>
<string>4.20.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.10</string>
<key>NSHumanReadableCopyright</key>
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION = 4.19.0.0
VERSION = 4.20.0.0
QMAKE_TARGET_COMPANY = "The pgAdmin Development Team"
QMAKE_TARGET_PRODUCT = "pgAdmin 4"
QMAKE_TARGET_DESCRIPTION = "pgAdmin 4 Desktop Runtime"
+5
View File
@@ -102,6 +102,11 @@ def get_chrome_version(args):
print('The specified Chrome executable output an unexpected '
'version string: {}.'.format(version_str))
sys.exit(1)
# On some linux distro `chrome--version` gives output like
# 'Google Chrome 80.0.3987.132 unknown\n'
# so we need to check and remove the unknown string from the version
if version_str.endswith("unknown\n"):
version_str = version_str.strip("unknown\n").strip()
chrome_version = '.'.join(version_str.split()[-1].split('.')[:-1])
+3
View File
@@ -22,6 +22,9 @@ indent_size = 2
[*.css]
indent_size = 2
[*.scss]
indent_size = 2
# Matches the exact file package.json
[{package.json}]
indent_size = 2
+2 -2
View File
@@ -51,7 +51,7 @@ APP_ICON = 'pg-icon'
# Application version number components
APP_RELEASE = 4
APP_REVISION = 19
APP_REVISION = 20
# Application version suffix, e.g. 'beta1', 'dev'. Usually an empty string
# for GA releases.
@@ -60,7 +60,7 @@ APP_SUFFIX = ''
# Numeric application version for upgrade checks. Should be in the format:
# [X]XYYZZ, where X is the release version, Y is the revision, with a leading
# zero if needed, and Z represents the suffix, with a leading zero if needed
APP_VERSION_INT = 41900
APP_VERSION_INT = 42000
# DO NOT CHANGE!
# The application version string, constructed from the components
+10 -2
View File
@@ -9,9 +9,9 @@
define(
['jquery', 'alertify', 'sources/pgadmin', 'sources/gettext',
'sources/url_for',
'sources/url_for','sources/utils',
],
function($, alertify, pgAdmin, gettext, url_for) {
function($, alertify, pgAdmin, gettext, url_for, commonUtils) {
pgAdmin = pgAdmin || window.pgAdmin || {};
/* Return back, this has been called more than once */
@@ -43,8 +43,16 @@ define(
build: function() {
alertify.pgDialogBuild.apply(this);
},
hooks:{
onshow:function(){
var container = $(this.elements.footer).find('button:not([disabled])');
commonUtils.findAndSetFocus(container);
},
},
prepare:function() {
this.setContent(this.message);
},
};
});
@@ -1266,13 +1266,14 @@ class ServerNode(PGChildNodeView):
data={
'status': 1,
'result': gettext(
'Named restore point created: {0}'.format(
restore_point_name))
'Named restore point created: {0}').format(
restore_point_name)
})
except Exception as e:
current_app.logger.error(
'Named restore point creation failed ({0})'.format(str(e))
current_app.logger.error(gettext(
'Named restore point creation failed ({0})').format(
str(e))
)
return internal_server_error(errormsg=str(e))
@@ -50,7 +50,7 @@ class CastModule(CollectionNodeModule):
"""
NODE_TYPE = 'cast'
COLLECTION_LABEL = 'Casts'
COLLECTION_LABEL = gettext('Casts')
def __init__(self, *args, **kwargs):
super(CastModule, self).__init__(*args, **kwargs)
@@ -111,10 +111,10 @@ define('pgadmin.node.event_trigger', [
id: 'enabled', label: gettext('Trigger enabled?'),
group: gettext('Definition'), mode: ['properties', 'edit','create'],
options: [
{label: 'Enable', value: 'O'},
{label: 'Disable', value: 'D'},
{label: 'Replica', value: 'R'},
{label: 'Always', value: 'A'},
{label: gettext('Enable'), value: 'O'},
{label: gettext('Disable'), value: 'D'},
{label: gettext('Replica'), value: 'R'},
{label: gettext('Always'), value: 'A'},
],
control: 'select2', select2: { allowClear: false, width: '100%' },
},{
@@ -125,9 +125,9 @@ define('pgadmin.node.event_trigger', [
id: 'eventname', label: gettext('Event'),
group: gettext('Definition'), cell: 'string',
options: [
{label: 'DDL COMMAND START', value: 'DDL_COMMAND_START'},
{label: 'DDL COMMAND END', value: 'DDL_COMMAND_END'},
{label: 'SQL DROP', value: 'SQL_DROP'},
{label: gettext('DDL COMMAND START'), value: 'DDL_COMMAND_START'},
{label: gettext('DDL COMMAND END'), value: 'DDL_COMMAND_END'},
{label: gettext('SQL DROP'), value: 'SQL_DROP'},
],
control: 'select2', select2: { allowClear: false, width: '100%' },
},{
@@ -52,8 +52,8 @@ class Properties:
execute_on_text = self.translate_execute_on_text(execute_on)
response = dict(
name=table_information_result['name'],
type=gettext('readable' if not table_information_result[
'writable'] else 'writable'),
type=gettext('readable') if not table_information_result[
'writable'] else gettext('writable'),
format_type=table_information_result['pg_encoding_to_char'],
format_options=table_information_result['fmtopts'],
external_options=table_information_result['options'],
@@ -65,14 +65,14 @@ class Properties:
@staticmethod
def translate_execute_on_text(execute_on):
if execute_on['type'] == 'host':
return 'host %s' % execute_on['value']
return gettext('host %s') % execute_on['value']
elif execute_on['type'] == 'per_host':
return 'per host'
return gettext('per host')
elif execute_on['type'] == 'master_only':
return 'master segment'
return gettext('master segment')
elif execute_on['type'] == 'all_segments':
return 'all segments'
return gettext('all segments')
elif execute_on['type'] == 'segment':
return '%s segment' % execute_on['value']
return gettext('%s segment') % execute_on['value']
elif execute_on['type'] == 'segments':
return '%d segments' % execute_on['value']
return gettext('%d segments') % execute_on['value']
@@ -501,7 +501,7 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def delete(self, gid, sid, did, scid, coid=None):
def delete(self, gid, sid, did, scid, coid=None, only_sql=False):
"""
This function will delete existing the collation object
@@ -511,6 +511,7 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
did: Database ID
scid: Schema ID
coid: Collation ID
only_sql: Return only sql if True
"""
if coid is None:
data = request.form if request.form else json.loads(
@@ -548,6 +549,11 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
nspname=data['schema'],
cascade=cascade,
conn=self.conn)
# Used for schema diff tool
if only_sql:
return SQL
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
@@ -685,7 +691,8 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
return SQL.strip('\n'), data['name']
@check_precondition
def sql(self, gid, sid, did, scid, coid):
def sql(self, gid, sid, did, scid, coid, diff_schema=None,
json_resp=True):
"""
This function will generates reverse engineered sql for collation
object
@@ -696,6 +703,8 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
did: Database ID
scid: Schema ID
coid: Collation ID
diff_schema: Target Schema for schema diff
json_resp: True then return json response
"""
SQL = render_template("/".join([self.template_path,
'properties.sql']),
@@ -710,6 +719,9 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
data = res['rows'][0]
if diff_schema:
data['schema'] = diff_schema
SQL = render_template("/".join([self.template_path,
'create.sql']),
data=data, conn=self.conn)
@@ -722,6 +734,9 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
nspname=data['schema'])
SQL = sql_header + '\n\n' + SQL.strip('\n')
if not json_resp:
return SQL
return ajax_response(response=SQL)
@check_precondition
@@ -793,5 +808,38 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
return res
def get_sql_from_diff(self, gid, sid, did, scid, oid, data=None,
diff_schema=None, drop_sql=False):
"""
This function is used to get the DDL/DML statements.
:param gid: Group ID
:param sid: Serve ID
:param did: Database ID
:param scid: Schema ID
:param oid: Collation ID
:param data: Difference data
:param diff_schema: Target Schema
:param drop_sql: True if need to drop the collation
:return:
"""
sql = ''
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, data=data, scid=scid,
coid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, coid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, coid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, coid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, CollationView)
CollationView.register_node_view(blueprint)
@@ -1,5 +1,23 @@
{% if data %}
{# Change object's owner #}
{% if (data.lc_collate and data.lc_type) or data.locale or data.copy_collation %}
-- WARNING:
-- We have found the difference in either of LC_COLLATE or LC_CTYPE or LOCALE,
-- so we need to drop the existing collation first and re-create it.
DROP COLLATION {{ conn|qtIdent(o_data.schema, o_data.name) }};
CREATE COLLATION {{ conn|qtIdent(o_data.schema, o_data.name) }}
{% if data.lc_collate and data.lc_type %}
(LC_COLLATE = {{ data.lc_collate|qtLiteral }}, LC_CTYPE = {{ data.lc_type|qtLiteral }});
{% endif %}
{% if data.locale %}
(LOCALE = {{ data.locale|qtLiteral }});
{% endif %}
{% if data.copy_collation %}
FROM {{ data.copy_collation }};
{% endif %}
{% endif %}
{% if data.owner and data.owner != o_data.owner %}
ALTER COLLATION {{ conn|qtIdent(o_data.schema, o_data.name) }}
OWNER TO {{ conn|qtIdent(data.owner) }};
@@ -179,6 +179,8 @@ class DomainView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
'compare': [{'get': 'compare'}, {'get': 'compare'}]
})
keys_to_ignore = ['oid', 'basensp', 'conoid', 'nspname', 'oid-2']
def validate_request(f):
"""
Works as a decorator.
@@ -584,7 +586,7 @@ AND relkind != 'c'))"""
)
@check_precondition
def delete(self, gid, sid, did, scid, doid=None):
def delete(self, gid, sid, did, scid, doid=None, only_sql=False):
"""
Drops the Domain object.
@@ -594,6 +596,7 @@ AND relkind != 'c'))"""
did: Database Id
scid: Schema Id
doid: Domain Id
only_sql: Return only sql if True
"""
if doid is None:
data = request.form if request.form else json.loads(
@@ -602,7 +605,7 @@ AND relkind != 'c'))"""
else:
data = {'ids': [doid]}
if self.cmd == 'delete':
if self.cmd == 'delete' or only_sql:
# This is a cascade operation
cascade = True
else:
@@ -634,6 +637,11 @@ AND relkind != 'c'))"""
SQL = render_template("/".join([self.template_path,
'delete.sql']),
name=name, basensp=basensp, cascade=cascade)
# Used for schema diff tool
if only_sql:
return SQL
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
@@ -684,7 +692,8 @@ AND relkind != 'c'))"""
)
@check_precondition
def sql(self, gid, sid, did, scid, doid=None, return_ajax_response=True):
def sql(self, gid, sid, did, scid, doid=None, diff_schema=None,
json_resp=True):
"""
Returns the SQL for the Domain object.
@@ -694,7 +703,8 @@ AND relkind != 'c'))"""
did: Database Id
scid: Schema Id
doid: Domain Id
return_ajax_response:
diff_schema: Target Schema for schema diff
json_resp: True then return json response
"""
SQL = render_template("/".join([self.template_path,
@@ -710,6 +720,9 @@ AND relkind != 'c'))"""
data = res['rows'][0]
if diff_schema:
data['basensp'] = diff_schema
# Get Type Length and Precision
data.update(self._parse_type(data['fulltype']))
@@ -737,7 +750,7 @@ AND relkind != 'c'))"""
""".format(self.qtIdent(self.conn, data['basensp'], data['name']))
SQL = sql_header + SQL
if not return_ajax_response:
if not json_resp:
return SQL.strip('\n')
return ajax_response(response=SQL.strip('\n'))
@@ -780,7 +793,7 @@ AND relkind != 'c'))"""
except Exception as e:
return internal_server_error(errormsg=str(e))
def get_sql(self, gid, sid, data, scid, doid=None):
def get_sql(self, gid, sid, data, scid, doid=None, is_schema_diff=False):
"""
Generates the SQL statements to create/update the Domain.
@@ -790,6 +803,7 @@ AND relkind != 'c'))"""
did: Database Id
scid: Schema Id
doid: Domain Id
is_schema_diff: True is function gets called from schema diff
"""
if doid is not None:
@@ -821,9 +835,19 @@ AND relkind != 'c'))"""
old_data['constraints'] = con_data
SQL = render_template(
"/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data)
# If fulltype or basetype or collname is changed while comparing
# two schemas then we need to drop domain and recreate it
if 'fulltype' in data or 'basetype' in data or 'collname' in data:
SQL = render_template(
"/".join([self.template_path, 'domain_schema_diff.sql']),
data=data, o_data=old_data)
else:
if is_schema_diff:
data['is_schema_diff'] = True
SQL = render_template(
"/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data)
return SQL.strip('\n'), data['name'] if 'name' in data else \
old_data['name']
else:
@@ -892,18 +916,43 @@ AND relkind != 'c'))"""
status, data = self._fetch_properties(did, scid, row['oid'])
if status:
if 'constraints' in data and len(data['constraints']) > 0:
for item in data['constraints']:
# Remove keys that should not be the part
# of comparision.
if 'conoid' in item:
item.pop('conoid')
if 'nspname' in item:
item.pop('nspname')
res[row['name']] = data
return res
def get_sql_from_diff(self, gid, sid, did, scid, oid, data=None,
diff_schema=None, drop_sql=False):
"""
This function is used to get the DDL/DML statements.
:param gid: Group ID
:param sid: Serve ID
:param did: Database ID
:param scid: Schema ID
:param oid: Collation ID
:param data: Difference data
:param diff_schema: Target Schema
:param drop_sql: True if need to drop the domains
:return:
"""
sql = ''
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, scid=scid,
data=data, doid=oid,
is_schema_diff=True)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, doid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, doid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, doid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, DomainView)
DomainView.register_node_view(blueprint)
@@ -0,0 +1,44 @@
{% import 'macros/schemas/security.macros' as SECLABEL %}
-- WARNING:
-- We have found the difference in either of datatype or collation,
-- so we need to drop the existing domain first and re-create it.
DROP DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }};
CREATE DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }}
AS {% if data.fulltype %}{{ data.fulltype }}{% else %}{{ o_data.fulltype }}{% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %}
COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %}
DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %}
NOT NULL{% endif %};
{% if data.owner %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};
{% endif %}
{% if data.constraints %}
{% for c in data.constraints.added %}{% if c.conname and c.consrc %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }}
ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% endif -%};
{% endfor -%}
{% for c in data.constraints.changed %}{% if c.conname and c.consrc %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }}
ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% endif -%};
{% endfor -%}
{% endif %}
{% if data.description %}
COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }}
IS '{{ data.description }}';{% endif -%}
{% if data.seclabels %}
{% for r in data.seclabels %}
{% if r.label and r.provider %}
{{ SECLABEL.SET(conn, 'DOMAIN', data.name, r.provider, r.label, data.basensp) }}{% endif -%}
{% endfor -%}
{% endif -%}
@@ -20,7 +20,7 @@ ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
SET DEFAULT {{ data.typdefault }};
{% elif data.typdefault == '' and o_data.typdefault %}
{% elif (data.typdefault == '' or data.typdefault == None) and data.typdefault != o_data.typdefault %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
DROP DEFAULT;
@@ -34,6 +34,19 @@ ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
DROP CONSTRAINT {{ conn|qtIdent(o_data['constraints'][c.conoid]['conname']) }};
{% endfor -%}
{% if data.is_schema_diff is defined and data.is_schema_diff %}
{% for c in data.constraints.changed %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
DROP CONSTRAINT {{ conn|qtIdent(c.conname) }};
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif -%};
{% if c.description is defined and c.description != '' %}
COMMENT ON CONSTRAINT {{ conn|qtIdent(c.conname) }} ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
IS {{ c.description|qtLiteral }};{% endif %}
{% endfor -%}
{% else %}
{% for c in data.constraints.changed %}
{% if c.conname and c.conname !=o_data['constraints'][c.conoid]['conname'] %}
@@ -46,6 +59,7 @@ ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
VALIDATE CONSTRAINT {{ conn|qtIdent(c.conname) }};
{% endif %}
{% endfor -%}
{% endif %}
{% for c in data.constraints.added %}
{% if c.conname and c.consrc %}
@@ -15,6 +15,6 @@ ALTER FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
COMMENT ON FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT ALL ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO postgres;
@@ -1 +1 @@
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
@@ -14,6 +14,6 @@ ALTER FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
COMMENT ON FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT ALL ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO postgres;
@@ -144,10 +144,6 @@
"grantee":"PUBLIC",
"grantor":"postgres",
"privileges":[{
"privilege_type":"a",
"privilege":true,
"with_grant":false
},{
"privilege_type":"r",
"privilege":true,
"with_grant":false
@@ -18,6 +18,6 @@ ALTER FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
COMMENT ON FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT ALL ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO postgres;
@@ -1 +1 @@
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
@@ -17,6 +17,6 @@ ALTER FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
COMMENT ON FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT ALL ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO postgres;
@@ -158,10 +158,6 @@
"grantee":"PUBLIC",
"grantor":"postgres",
"privileges":[{
"privilege_type":"a",
"privilege":true,
"with_grant":false
},{
"privilege_type":"r",
"privilege":true,
"with_grant":false
@@ -16,6 +16,6 @@ ALTER FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
COMMENT ON FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT ALL ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO enterprisedb;
@@ -1 +1 @@
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
@@ -15,6 +15,6 @@ ALTER FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
COMMENT ON FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT ALL ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO enterprisedb;
@@ -143,10 +143,6 @@
"grantee":"PUBLIC",
"grantor":"enterprisedb",
"privileges":[{
"privilege_type":"a",
"privilege":true,
"with_grant":false
},{
"privilege_type":"r",
"privilege":true,
"with_grant":false
@@ -18,6 +18,6 @@ ALTER FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
COMMENT ON FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT ALL ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO enterprisedb;
@@ -1 +1 @@
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
@@ -17,6 +17,6 @@ ALTER FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
COMMENT ON FOREIGN TABLE public."FT1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';
GRANT INSERT, SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT SELECT ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO PUBLIC;
GRANT ALL ON TABLE public."FT1_$%{}[]()&*^!@""'`\/#" TO enterprisedb;
@@ -158,10 +158,6 @@
"grantee":"PUBLIC",
"grantor":"enterprisedb",
"privileges":[{
"privilege_type":"a",
"privilege":true,
"with_grant":false
},{
"privilege_type":"r",
"privilege":true,
"with_grant":false
@@ -532,7 +532,7 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def delete(self, gid, sid, did, scid, cfgid=None):
def delete(self, gid, sid, did, scid, cfgid=None, only_sql=False):
"""
This function will drop the FTS Configuration object
:param gid: group id
@@ -540,6 +540,7 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param cfgid: FTS Configuration id
:param only_sql: Return only sql if True
"""
if cfgid is None:
data = request.form if request.form else json.loads(
@@ -587,6 +588,10 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
cascade=cascade
)
# Used for schema diff tool
if only_sql:
return sql
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@@ -869,7 +874,8 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def sql(self, gid, sid, did, scid, cfgid):
def sql(self, gid, sid, did, scid, cfgid, diff_schema=None,
json_resp=True):
"""
This function will reverse generate sql for sql panel
:param gid: group id
@@ -877,6 +883,8 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param cfgid: FTS Configuration id
:param diff_schema: Target Schema for schema diff
:param json_resp: True then return json response
"""
try:
sql = render_template(
@@ -901,6 +909,25 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
"FTS Configuration node.")
)
# Used for schema diff tool
if diff_schema:
data = {'schema': scid}
# Fetch schema name from schema oid
sql = render_template("/".join([self.template_path,
'schema.sql']),
data=data,
conn=self.conn,
)
status, schema = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=schema)
res = res.replace(schema, diff_schema)
if not json_resp:
return res
return ajax_response(response=res)
except Exception as e:
@@ -970,5 +997,38 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
return res
def get_sql_from_diff(self, gid, sid, did, scid, oid, data=None,
diff_schema=None, drop_sql=False):
"""
This function is used to get the DDL/DML statements.
:param gid: Group ID
:param sid: Serve ID
:param did: Database ID
:param scid: Schema ID
:param oid: Collation ID
:param data: Difference data
:param diff_schema: Target Schema
:param drop_sql: True if need to drop the fts configuration
:return:
"""
sql = ''
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid,
data=data, cfgid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, cfgid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, cfgid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, cfgid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, FtsConfigurationView)
FtsConfigurationView.register_node_view(blueprint)
@@ -212,7 +212,7 @@ define('pgadmin.node.fts_configuration', [
' </div>',
' <div class="col-6" header="token"></div>',
' <div class="col-2">',
' <button class="btn btn-sm-sq btn-secondary add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
' <button class="btn btn-sm-sq btn-secondary add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ><span class="sr-only">Add Token</span></button>',
' </div>',
' </div>',
' </div>',
@@ -527,7 +527,7 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def delete(self, gid, sid, did, scid, dcid=None):
def delete(self, gid, sid, did, scid, dcid=None, only_sql=False):
"""
This function will drop the FTS Dictionary object
:param gid: group id
@@ -535,6 +535,7 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param dcid: FTS Dictionary id
:param only_sql: Return only sql if True
"""
if dcid is None:
data = request.form if request.form else json.loads(
@@ -581,6 +582,10 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
cascade=cascade
)
# Used for schema diff tool
if only_sql:
return sql
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@@ -766,7 +771,8 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def sql(self, gid, sid, did, scid, dcid):
def sql(self, gid, sid, did, scid, dcid, diff_schema=None,
json_resp=True):
"""
This function will reverse generate sql for sql panel
:param gid: group id
@@ -774,6 +780,8 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param dcid: FTS Dictionary id
:param diff_schema: Target Schema for schema diff
:param json_resp: True then return json response
"""
sql = render_template(
@@ -819,6 +827,9 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
# Replace schema oid with schema name
res['rows'][0]['schema'] = schema
if diff_schema:
res['rows'][0]['schema'] = diff_schema
sql = render_template("/".join([self.template_path, 'create.sql']),
data=res['rows'][0],
conn=self.conn, is_displaying=True)
@@ -832,6 +843,9 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
sql = sql_header + sql
if not json_resp:
return sql
return ajax_response(response=sql.strip('\n'))
@check_precondition
@@ -897,5 +911,38 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
return res
def get_sql_from_diff(self, gid, sid, did, scid, oid, data=None,
diff_schema=None, drop_sql=False):
"""
This function is used to get the DDL/DML statements.
:param gid: Group ID
:param sid: Serve ID
:param did: Database ID
:param scid: Schema ID
:param oid: Collation ID
:param data: Difference data
:param diff_schema: Target Schema
:param drop_sql: True if need to drop the fts configuration
:return:
"""
sql = ''
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid,
data=data, dcid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, dcid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, dcid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, dcid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, FtsDictionaryView)
FtsDictionaryView.register_node_view(blueprint)
@@ -476,7 +476,7 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def delete(self, gid, sid, did, scid, pid=None):
def delete(self, gid, sid, did, scid, pid=None, only_sql=False):
"""
This function will drop the fts_parser object
:param gid: group id
@@ -484,6 +484,7 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param pid: fts tempate id
:param only_sql: Return only sql if True
"""
if pid is None:
data = request.form if request.form else json.loads(
@@ -530,6 +531,10 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
cascade=cascade
)
# Used for schema diff tool
if only_sql:
return sql
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@@ -813,7 +818,8 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def sql(self, gid, sid, did, scid, pid):
def sql(self, gid, sid, did, scid, pid, diff_schema=None,
json_resp=True):
"""
This function will reverse generate sql for sql panel
:param gid: group id
@@ -821,6 +827,8 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param pid: fts tempate id
:param diff_schema: Target Schema for schema diff
:param json_resp: True then return json response
"""
try:
sql = render_template(
@@ -846,6 +854,25 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
)
)
# Used for schema diff tool
if diff_schema:
data = {'schema': scid}
# Fetch schema name from schema oid
sql = render_template("/".join([self.template_path,
'schema.sql']),
data=data,
conn=self.conn,
)
status, schema = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=schema)
res = res.replace(schema, diff_schema)
if not json_resp:
return res
return ajax_response(response=res)
except Exception as e:
@@ -915,5 +942,38 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
return res
def get_sql_from_diff(self, gid, sid, did, scid, oid, data=None,
diff_schema=None, drop_sql=False):
"""
This function is used to get the DDL/DML statements.
:param gid: Group ID
:param sid: Serve ID
:param did: Database ID
:param scid: Schema ID
:param oid: Collation ID
:param data: Difference data
:param diff_schema: Target Schema
:param drop_sql: True if need to drop the fts configuration
:return:
"""
sql = ''
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid,
data=data, pid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, pid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, FtsParserView)
FtsParserView.register_node_view(blueprint)
@@ -15,6 +15,23 @@ ALTER TEXT SEARCH PARSER {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(o_data.n
ALTER TEXT SEARCH PARSER {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
SET SCHEMA {{data.schema}};
{% endif %}
{# Schema Diff SQL for FTS PARSER #}
{% if data.prsstart or data.prstoken or data.prsend or data.prslextype or data.prsheadline %}
-- WARNING:
-- We have found the difference in either of START or GETTOKEN or END or
-- LEXTYPES or HEADLINE, so we need to drop the existing parser first
-- and re-create it.
DROP TEXT SEARCH PARSER {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}};
CREATE TEXT SEARCH PARSER {{ conn|qtIdent(o_data.schema, name) }} (
START = {% if data.prsstart is defined %}{{data.prsstart}}{% else %}{{o_data.prsstart}}{% endif %},
GETTOKEN = {% if data.prstoken is defined %}{{data.prstoken}}{% else %}{{o_data.prstoken}}{% endif %},
END = {% if data.prsend is defined %}{{data.prsend}}{% else %}{{o_data.prsend}}{% endif %},
LEXTYPES = {% if data.prslextype is defined %}{{data.prslextype}}{% else %}{{o_data.prslextype}}{% endif %}{% if (data.prsheadline and data.prsheadline != '-') or (o_data.prsheadline and o_data.prsheadline != '-') %},
HEADLINE = {% if data.prsheadline is defined %}{{data.prsheadline}}{% else %}{{o_data.prsheadline}}{% endif %}{% endif %}
);
{% endif %}
{% if "description" in data and data.description != o_data.description %}
COMMENT ON TEXT SEARCH PARSER {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
IS {{ data.description|qtLiteral }};
@@ -443,7 +443,7 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def delete(self, gid, sid, did, scid, tid=None):
def delete(self, gid, sid, did, scid, tid=None, only_sql=False):
"""
This function will drop the fts_template object
:param gid: group id
@@ -451,6 +451,7 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param tid: fts tempate id
:param only_sql: Return only sql if True
"""
if tid is None:
data = request.form if request.form else json.loads(
@@ -493,6 +494,10 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
cascade=cascade
)
# Used for schema diff tool
if only_sql:
return sql
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@@ -691,7 +696,8 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def sql(self, gid, sid, did, scid, tid):
def sql(self, gid, sid, did, scid, tid, diff_schema=None,
json_resp=True):
"""
This function will reverse generate sql for sql panel
:param gid: group id
@@ -699,6 +705,8 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param tid: fts tempate id
:param diff_schema: Target Schema for schema diff
:param json_resp: True then return json response
"""
sql = render_template(
"/".join([self.template_path, 'sql.sql']),
@@ -721,6 +729,25 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
"FTS Template node.")
)
# Used for schema diff tool
if diff_schema:
data = {'schema': scid}
# Fetch schema name from schema oid
sql = render_template("/".join([self.template_path,
'schema.sql']),
data=data,
conn=self.conn,
)
status, schema = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=schema)
res = res.replace(schema, diff_schema)
if not json_resp:
return res
return ajax_response(response=res)
@check_precondition
@@ -786,5 +813,38 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
return res
def get_sql_from_diff(self, gid, sid, did, scid, oid, data=None,
diff_schema=None, drop_sql=False):
"""
This function is used to get the DDL/DML statements.
:param gid: Group ID
:param sid: Serve ID
:param did: Database ID
:param scid: Schema ID
:param oid: Collation ID
:param data: Difference data
:param diff_schema: Target Schema
:param drop_sql: True if need to drop the fts configuration
:return:
"""
sql = ''
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid,
data=data, tid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, tid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, FtsTemplateView)
FtsTemplateView.register_node_view(blueprint)
@@ -15,6 +15,21 @@ ALTER TEXT SEARCH TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(o_data
ALTER TEXT SEARCH TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
SET SCHEMA {{conn|qtIdent(data.schema)}};
{% endif %}
{# Schema Diff SQL for FTS PARSER #}
{% if data.tmplinit or data.tmpllexize %}
-- WARNING:
-- We have found the difference in either of INIT or LEXIZE,
-- so we need to drop the existing template first and re-create it.
DROP TEXT SEARCH TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}};
CREATE TEXT SEARCH TEMPLATE {{ conn|qtIdent(o_data.schema, name) }} (
{% if data.tmplinit and data.tmplinit != '-'%}
INIT = {{data.tmplinit}},
{% endif %}
LEXIZE = {% if data.tmpllexize is defined %}{{data.tmpllexize}}{% else %}{{o_data.tmpllexize}}{% endif %}
);
{% endif %}
{% if 'description' in data and data.description != o_data.description %}
COMMENT ON TEXT SEARCH TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
IS {{ data.description|qtLiteral }};
@@ -220,7 +220,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
})
keys_to_ignore = ['oid', 'proowner', 'typnsp', 'xmin', 'prokind',
'proisagg', 'pronamespace', 'proargdefaults']
'proisagg', 'pronamespace', 'proargdefaults',
'prorettype', 'proallargtypes', 'proacl', 'oid-2']
@property
def required_args(self):
@@ -1125,7 +1126,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
SQL = re.sub('\n{2,}', '\n\n', SQL)
return SQL
def _get_sql(self, gid, sid, did, scid, data, fnid=None, is_sql=False):
def _get_sql(self, gid, sid, did, scid, data, fnid=None, is_sql=False,
is_schema_diff=False):
"""
Generates the SQL statements to create/update the Function.
@@ -1239,8 +1241,11 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
for v in data['variables']['added']:
chngd_variables[v['name']] = v['value']
for v in old_data['variables']:
old_data['chngd_variables'][v['name']] = v['value']
# In case of schema diff we don't want variables from
# old data
if not is_schema_diff:
for v in old_data['variables']:
old_data['chngd_variables'][v['name']] = v['value']
# Prepare final dict of new and old variables
for name, val in old_data['chngd_variables'].items():
@@ -1624,7 +1629,15 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
if data:
if diff_schema:
data['schema'] = diff_schema
status, sql = self._get_sql(gid, sid, did, scid, data, oid)
status, sql = self._get_sql(gid, sid, did, scid, data, oid, False,
True)
# Check if return type is changed then we need to drop the
# function first and then recreate it.
drop_fun_sql = ''
if 'prorettypename' in data:
drop_fun_sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, fnid=oid, only_sql=True)
sql = drop_fun_sql + '\n' + sql
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
@@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0'%}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
@@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %}
@@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -28,7 +29,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
@@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -29,7 +30,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
@@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
@@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -27,7 +28,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
@@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %}
@@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -28,7 +29,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
@@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -29,7 +30,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
@@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
@@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %}
{% endif -%}
)
RETURNS {{ o_data.prorettypename }}
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
@@ -27,7 +28,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
@@ -113,7 +113,7 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare):
'dependent': [{'get': 'dependents'}]
})
keys_to_ignore = ['oid', 'schema', 'xmin']
keys_to_ignore = ['oid', 'schema', 'xmin', 'oid-2']
def check_precondition(action=None):
"""
@@ -11,8 +11,6 @@
import simplejson as json
import re
import copy
import random
import pgadmin.browser.server_groups.servers.databases as database
from flask import render_template, request, jsonify, url_for, current_app
@@ -25,11 +23,6 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
from .utils import BaseTableView
from pgadmin.utils.preferences import Preferences
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\
directory_diff
from pgadmin.tools.schema_diff.model import SchemaDiffModel
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints.foreign_key import utils as fkey_utils
from .schema_diff_utils import SchemaDiffTableCompare
@@ -1212,8 +1205,6 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
json_resp = kwargs['json_resp'] if 'json_resp' in kwargs else True
diff_schema = kwargs['diff_schema'] if 'diff_schema' in kwargs else\
None
schema_diff_table = kwargs['schema_diff_table'] if\
'schema_diff_table' in kwargs else None
if diff_data:
return self._fetch_sql(did, scid, tid, diff_data, json_resp)
@@ -1239,24 +1230,8 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
if diff_schema:
data['schema'] = diff_schema
if schema_diff_table:
data['orig_name'] = data['name']
data['name'] = 'schema_diff_temp_{0}'.format(
random.randint(1, 9999999))
sql, partition_sql = BaseTableView.get_reverse_engineered_sql(
self, did, scid, tid, main_sql, data, json_resp,
diff_partition_sql=True)
else:
sql, partition_sql = BaseTableView.get_reverse_engineered_sql(
self, did, scid, tid, main_sql, data, json_resp)
if schema_diff_table:
# If partition tables have different partitions
sql += render_template(
"/".join([self.table_template_path, 'schema_diff.sql']),
conn=self.conn, data=data, partition_sql=partition_sql
)
sql, partition_sql = BaseTableView.get_reverse_engineered_sql(
self, did, scid, tid, main_sql, data, json_resp)
return sql
@@ -1625,7 +1600,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
return sql
@BaseTableView.check_precondition
def fetch_tables(self, sid, did, scid, tid=None, keys_to_remove=None):
def fetch_tables(self, sid, did, scid, tid=None):
"""
This function will fetch the list of all the tables
and will be used by schema diff.
@@ -1634,9 +1609,13 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
:param did: Database Id
:param scid: Schema Id
:param tid: Table Id
:param keys_to_remove: Table columns to be removed from the dataset
:return: Table dataset
"""
sub_modules = ['index', 'rule', 'trigger']
if self.manager.server_type == 'ppas' and \
self.manager.version >= 120000:
sub_modules.append('compound_trigger')
if tid:
status, data = self._fetch_properties(did, scid, tid)
@@ -1647,7 +1626,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
data = super(TableView, self).properties(
0, sid, did, scid, tid, data, False
)
self.remove_keys_for_comparision(data, keys_to_remove)
return data
else:
@@ -1667,7 +1646,17 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
0, sid, did, scid, row['oid'], data, False
)
self.remove_keys_for_comparision(data, keys_to_remove)
# Get sub module data of a specified table for object
# comparison
for module in sub_modules:
module_view = SchemaDiffRegistry.get_node_view(module)
if module_view.blueprint.server_type is None or \
self.manager.server_type in \
module_view.blueprint.server_type:
sub_data = module_view.fetch_objects_to_compare(
sid=sid, did=did, scid=scid, tid=row['oid'],
oid=None)
data[module] = sub_data
res[row['name']] = data
return res
@@ -29,6 +29,8 @@ from pgadmin.utils import IS_PY2
from pgadmin.utils.compile_template_name import compile_template_path
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.tools.schema_diff.directory_compare import directory_diff,\
parce_acl
# If we are in Python3
@@ -252,7 +254,7 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare):
# Schema Diff: Keys to ignore while comparing
keys_to_ignore = ['oid', 'xmin', 'nspname', 'tfunction',
'tgrelid', 'tgfoid']
'tgrelid', 'tgfoid', 'oid-2']
def check_precondition(f):
"""
@@ -938,8 +940,7 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare):
return SQL
@check_precondition
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None,
ignore_keys=False):
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None):
"""
This function will fetch the list of all the triggers for
specified schema id.
@@ -969,14 +970,60 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare):
for row in triggers['rows']:
status, data = self._fetch_properties(tid, row['oid'])
if status:
if ignore_keys:
for key in self.keys_to_ignore:
if key in data:
del data[key]
res[row['name']] = data
return res
def ddl_compare(self, **kwargs):
"""
This function returns the DDL/DML statements based on the
comparison status.
:param kwargs:
:return:
"""
src_params = kwargs.get('source_params')
tgt_params = kwargs.get('target_params')
source = kwargs.get('source')
target = kwargs.get('target')
target_schema = kwargs.get('target_schema')
comp_status = kwargs.get('comp_status')
diff = ''
if comp_status == 'source_only':
diff = self.get_sql_from_diff(gid=src_params['gid'],
sid=src_params['sid'],
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
oid=source['oid'],
diff_schema=target_schema)
elif comp_status == 'target_only':
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
drop_sql=True)
elif comp_status == 'different':
diff_dict = directory_diff(
source, target,
ignore_keys=self.keys_to_ignore, difference={}
)
diff_dict.update(parce_acl(source, target))
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
data=diff_dict)
return diff
SchemaDiffRegistry(blueprint.node_type, CompoundTriggerView, 'table')
CompoundTriggerView.register_node_view(blueprint)
@@ -450,7 +450,7 @@ define('pgadmin.node.exclusion_constraint', [
titleTmpl = _.template([
'<div class="subnode-header">',
' <label class="control-label pg-el-sm-10"><%-label%></label>',
' <button class="btn btn-sm-sq btn-secondary add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> title="' + _('Add new row') + '"></button>',
' <button class="btn btn-sm-sq btn-secondary add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> title="' + gettext('Add new row') + '"></button>',
'</div>'].join('\n')),
$gridBody =
$('<div class=\'pgadmin-control-group backgrid form-group col-12 object subnode\'></div>').append(
@@ -378,7 +378,7 @@ define('pgadmin.node.foreign_key', [
titleTmpl = _.template([
'<div class="subnode-header">',
' <label class="control-label pg-el-sm-10"><%-label%></label>',
' <button class="btn btn-sm-sq btn-secondary add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> title="' + _('Add new row') + '"></button>',
' <button class="btn btn-sm-sq btn-secondary add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> title="' + gettext('Add new row') + '"></button>',
'</div>'].join('\n')),
$gridBody =
$('<div class=\'pgadmin-control-group backgrid form-group col-12 object subnode\'></div>').append(
@@ -82,10 +82,10 @@ def get_foreign_keys(conn, tid, fkid=None, template_path=None):
coveringindex = search_coveringindex(conn, tid, cols)
fk['coveringindex'] = coveringindex
if coveringindex:
fk['autoindex'] = True
fk['autoindex'] = False
fk['hasindex'] = True
else:
fk['autoindex'] = False
fk['autoindex'] = True
fk['hasindex'] = False
return True, result['rows']
@@ -26,9 +26,7 @@ from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.utils import IS_PY2
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\
directory_diff
from pgadmin.tools.schema_diff.model import SchemaDiffModel
from pgadmin.tools.schema_diff.directory_compare import directory_diff
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.browser.server_groups.servers.databases.schemas. \
tables.indexes import utils as index_utils
@@ -235,8 +233,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
# Schema Diff: Keys to ignore while comparing
keys_to_ignore = ['oid', 'relowner', 'schema',
'indrelid', 'nspname'
]
'indrelid', 'nspname', 'oid-2']
def check_precondition(f):
"""
@@ -830,11 +827,8 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
def get_sql_from_index_diff(self, sid, did, scid, tid, idx, data=None,
diff_schema=None, drop_req=False):
tmp_idx = idx
schema = ''
sql = ''
if data:
schema = self.schema
data['schema'] = self.schema
data['nspname'] = self.schema
data['table'] = self.table
@@ -987,8 +981,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None,
ignore_keys=False):
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None):
"""
This function will fetch the list of all the indexes for
specified schema id.
@@ -996,6 +989,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
:param sid: Server Id
:param did: Database Id
:param scid: Schema Id
:param oid: Index Id
:return:
"""
@@ -1013,10 +1007,6 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
status, data = self._fetch_properties(did, tid,
row['oid'])
if status:
if ignore_keys:
for key in self.keys_to_ignore:
if key in data:
del data[key]
res[row['name']] = data
else:
status, data = self._fetch_properties(did, tid,
@@ -1030,55 +1020,35 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
def ddl_compare(self, **kwargs):
"""
This function will compare index properties and
return the difference of SQL
This function returns the DDL/DML statements based on the
comparison status.
:param kwargs:
:return:
"""
src_sid = kwargs.get('source_sid')
src_did = kwargs.get('source_did')
src_scid = kwargs.get('source_scid')
src_tid = kwargs.get('source_tid')
src_oid = kwargs.get('source_oid')
tar_sid = kwargs.get('target_sid')
tar_did = kwargs.get('target_did')
tar_scid = kwargs.get('target_scid')
tar_tid = kwargs.get('target_tid')
tar_oid = kwargs.get('target_oid')
src_params = kwargs.get('source_params')
tgt_params = kwargs.get('target_params')
source = kwargs.get('source')
target = kwargs.get('target')
target_schema = kwargs.get('target_schema')
comp_status = kwargs.get('comp_status')
source = ''
target = ''
diff = ''
status, target_schema = self.get_schema(tar_sid,
tar_did,
tar_scid
)
if not status:
return internal_server_error(errormsg=target_schema)
if comp_status == SchemaDiffModel.COMPARISON_STATUS['source_only']:
diff = self.get_sql_from_index_diff(sid=src_sid,
did=src_did, scid=src_scid,
tid=src_tid, idx=src_oid,
if comp_status == 'source_only':
diff = self.get_sql_from_index_diff(sid=src_params['sid'],
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
idx=source['oid'],
diff_schema=target_schema)
elif comp_status == 'target_only':
diff = self.delete(gid=1, sid=tgt_params['sid'],
did=tgt_params['did'], scid=tgt_params['scid'],
tid=tgt_params['tid'], idx=target['oid'],
only_sql=True)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['target_only']:
diff = self.delete(gid=1, sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_tid,
idx=tar_oid, only_sql=True)
else:
source = self.fetch_objects_to_compare(sid=src_sid, did=src_did,
scid=src_scid, tid=src_tid,
oid=src_oid)
target = self.fetch_objects_to_compare(sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_tid,
oid=tar_oid)
if not (source or target):
return None
elif comp_status == 'different':
diff_dict = directory_diff(
source, target, ignore_keys=self.keys_to_ignore,
difference={}
@@ -1102,19 +1072,19 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
create_req = True
if create_req:
diff = self.get_sql_from_index_diff(sid=tar_sid,
did=tar_did,
scid=tar_scid,
tid=tar_tid,
idx=tar_oid,
diff = self.get_sql_from_index_diff(sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
idx=target['oid'],
diff_schema=target_schema,
drop_req=True)
else:
diff = self.get_sql_from_index_diff(sid=tar_sid,
did=tar_did,
scid=tar_scid,
tid=tar_tid,
idx=tar_oid,
diff = self.get_sql_from_index_diff(sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
idx=target['oid'],
data=diff_dict)
return diff
@@ -10,9 +10,10 @@
""" Implements Partitions Node """
import re
import random
import simplejson as json
import pgadmin.browser.server_groups.servers.databases.schemas as schema
from flask import render_template, request
from flask import render_template, request, current_app
from flask_babelex import gettext
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
import DataTypeReader, VacuumSettings
@@ -21,13 +22,9 @@ from pgadmin.utils.ajax import internal_server_error, \
from pgadmin.browser.server_groups.servers.databases.schemas.tables.utils \
import BaseTableView
from pgadmin.browser.collection import CollectionNodeModule
from pgadmin.utils.ajax import make_json_response, precondition_required
from config import PG_DEFAULT_DRIVER
from pgadmin.utils.ajax import make_json_response
from pgadmin.browser.utils import PGChildModule
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\
directory_diff
from pgadmin.tools.schema_diff.model import SchemaDiffModel
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
@@ -226,7 +223,7 @@ class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings,
# Schema Diff: Keys to ignore while comparing
keys_to_ignore = ['oid', 'schema', 'vacuum_table',
'vacuum_toast', 'edit_types']
'vacuum_toast', 'edit_types', 'oid-2']
def get_children_nodes(self, manager, **kwargs):
nodes = []
@@ -464,58 +461,57 @@ class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings,
@BaseTableView.check_precondition
def get_sql_from_diff(self, **kwargs):
"""
This function will create sql on the basis the difference of 2 tables
This function is used to get the DDL/DML statements for
partitions.
:param kwargs:
:return:
"""
data = dict()
res = None
sid = kwargs['sid']
did = kwargs['did']
scid = kwargs['scid']
tid = kwargs['tid']
ptid = kwargs['ptid']
diff_data = kwargs['diff_data'] if 'diff_data' in kwargs else None
json_resp = kwargs['json_resp'] if 'json_resp' in kwargs else True
diff_schema = kwargs['diff_schema'] if 'diff_schema' in kwargs else\
None
if diff_data:
SQL = render_template("/".join([self.partition_template_path,
'properties.sql']),
did=did, scid=scid, tid=tid,
ptid=ptid, datlastsysoid=self.datlastsysoid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
source_data = kwargs['source_data'] if 'source_data' in kwargs \
else None
target_data = kwargs['target_data'] if 'target_data' in kwargs \
else None
SQL, name = self.get_sql(did, scid, ptid, diff_data, res)
SQL = re.sub('\n{2,}', '\n\n', SQL)
SQL = SQL.strip('\n')
return SQL
else:
main_sql = []
# Store the original name and create a temporary name for
# the partitioned(base) table.
target_data['orig_name'] = target_data['name']
target_data['name'] = 'temp_partitioned_{0}'.format(
random.randint(1, 9999999))
# For PG/EPAS 11 and above when we copy the data from original
# table to temporary table for schema diff, we will have to create
# a default partition to prevent the data loss.
target_data['default_partition_name'] = \
target_data['orig_name'] + '_default'
SQL = render_template("/".join([self.partition_template_path,
'properties.sql']),
did=did, scid=scid, tid=tid,
ptid=ptid, datlastsysoid=self.datlastsysoid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
# Copy the partition scheme from source to target.
if 'partition_scheme' in source_data:
target_data['partition_scheme'] = source_data['partition_scheme']
if len(res['rows']) == 0:
return gone(gettext(
"The specified partitioned table could not be found."))
partition_data = dict()
partition_data['name'] = target_data['name']
partition_data['schema'] = target_data['schema']
partition_data['partition_type'] = source_data['partition_type']
partition_data['default_partition_header'] = \
'-- Create a default partition to prevent the data loss.\n' \
'-- It helps when none of the partitions of a relation\n' \
'-- matches the inserted data.'
data = res['rows'][0]
# Create temporary name for partitions
for item in source_data['partitions']:
item['temp_partition_name'] = 'partition_{0}'.format(
random.randint(1, 9999999))
if diff_schema:
data['schema'] = diff_schema
data['parent_schema'] = diff_schema
partition_data['partitions'] = source_data['partitions']
return BaseTableView.get_reverse_engineered_sql(self, did,
scid, ptid,
main_sql, data,
False)
partition_sql = self.get_partitions_sql(partition_data,
schema_diff=True)
return render_template(
"/".join([self.partition_template_path, 'partition_diff.sql']),
conn=self.conn, data=target_data, partition_sql=partition_sql,
partition_data=partition_data
)
@BaseTableView.check_precondition
def detach(self, gid, sid, did, scid, tid, ptid):
@@ -775,58 +771,25 @@ class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings,
def ddl_compare(self, **kwargs):
"""
This function will compare index properties and
return the difference of SQL
This function returns the DDL/DML statements based on the
comparison status.
:param kwargs:
:return:
"""
src_sid = kwargs.get('source_sid')
src_did = kwargs.get('source_did')
src_scid = kwargs.get('source_scid')
src_tid = kwargs.get('source_tid')
src_oid = kwargs.get('source_oid')
tar_sid = kwargs.get('target_sid')
tar_did = kwargs.get('target_did')
tar_scid = kwargs.get('target_scid')
tar_tid = kwargs.get('target_tid')
tar_oid = kwargs.get('target_oid')
comp_status = kwargs.get('comp_status')
tgt_params = kwargs.get('target_params')
parent_source_data = kwargs.get('parent_source_data')
parent_target_data = kwargs.get('parent_target_data')
source = ''
target = ''
diff = ''
diff = self.get_sql_from_diff(sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
source_data=parent_source_data,
target_data=parent_target_data)
status, target_schema = self.get_schema_for_schema_diff(tar_sid,
tar_did,
tar_scid
)
if not status:
return internal_server_error(errormsg=target_schema)
if comp_status == SchemaDiffModel.COMPARISON_STATUS['source_only']:
diff = self.get_sql_from_diff(sid=src_sid,
did=src_did, scid=src_scid,
tid=src_tid, ptid=src_oid,
diff_schema=target_schema)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['target_only']:
SQL = render_template("/".join([self.partition_template_path,
'properties.sql']),
did=did, scid=scid, tid=tid,
ptid=ptid, datlastsysoid=self.datlastsysoid)
status, res = self.conn.execute_dict(SQL)
SQL = render_template(
"/".join([self.table_template_path, 'properties.sql']),
did=tar_did, scid=tar_scid, tid=tar_oid,
datlastsysoid=self.datlastsysoid
)
status, res = self.conn.execute_dict(SQL)
if status:
self.cmd = 'delete'
diff = super(PartitionsView, self).get_delete_sql(res)
self.cmd = None
return diff
return diff + '\n'
SchemaDiffRegistry(blueprint.node_type, PartitionsView, 'table')
@@ -71,12 +71,12 @@ function(
// To enable/disable all triggers for the table
name: 'enable_all_triggers', node: 'partition', module: this,
applies: ['object', 'context'], callback: 'enable_triggers_on_table',
category: 'Trigger(s)', priority: 4, label: gettext('Enable All'),
category: gettext('Trigger(s)'), priority: 4, label: gettext('Enable All'),
icon: 'fa fa-check', enable : 'canCreate_with_trigger_enable',
},{
name: 'disable_all_triggers', node: 'partition', module: this,
applies: ['object', 'context'], callback: 'disable_triggers_on_table',
category: 'Trigger(s)', priority: 4, label: gettext('Disable All'),
category: gettext('Trigger(s)'), priority: 4, label: gettext('Disable All'),
icon: 'fa fa-times', enable : 'canCreate_with_trigger_disable',
},{
name: 'reset_table_stats', node: 'partition', module: this,
@@ -28,6 +28,8 @@ from pgadmin.utils.compile_template_name import compile_template_path
from pgadmin.utils import IS_PY2
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.tools.schema_diff.directory_compare import directory_diff,\
parce_acl
# If we are in Python3
if not IS_PY2:
@@ -184,7 +186,7 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
})
# Schema Diff: Keys to ignore while comparing
keys_to_ignore = ['oid', 'schema', 'definition']
keys_to_ignore = ['oid', 'schema', 'definition', 'oid-2']
def check_precondition(f):
"""
@@ -520,7 +522,8 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
@check_precondition
def get_sql_from_diff(self, gid, sid, did, scid, tid, oid, data=None,
diff_schema=None, drop_sql=False):
source_schema=None, diff_schema=None,
drop_sql=False):
if drop_sql:
SQL = self.delete(gid=gid, sid=sid, did=did,
@@ -541,6 +544,11 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
SQL = ''
if data:
if source_schema:
if 'statements' in data:
# Replace the source schema with the target schema
data['statements'] = data['statements'].replace(
source_schema, diff_schema)
old_data = res_data
SQL = render_template(
"/".join([self.template_path, 'update.sql']),
@@ -548,6 +556,11 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
)
else:
if diff_schema:
if 'statements' in res_data:
# Replace the source schema with the target schema
res_data['statements'] = \
res_data['statements'].replace(
res_data['schema'], diff_schema)
res_data['schema'] = diff_schema
SQL = render_template("/".join(
@@ -595,8 +608,7 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None,
ignore_keys=False):
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None):
"""
This function will fetch the list of all the rules for
specified schema id.
@@ -628,13 +640,61 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
for row in rules['rows']:
status, data = self._fetch_properties(row['oid'])
if status:
if ignore_keys:
for key in self.keys_to_ignore:
if key in data:
del data[key]
res[row['name']] = data
return res
def ddl_compare(self, **kwargs):
"""
This function returns the DDL/DML statements based on the
comparison status.
:param kwargs:
:return:
"""
src_params = kwargs.get('source_params')
tgt_params = kwargs.get('target_params')
source = kwargs.get('source')
target = kwargs.get('target')
target_schema = kwargs.get('target_schema')
comp_status = kwargs.get('comp_status')
diff = ''
if comp_status == 'source_only':
diff = self.get_sql_from_diff(gid=src_params['gid'],
sid=src_params['sid'],
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
oid=source['oid'],
diff_schema=target_schema)
elif comp_status == 'target_only':
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
drop_sql=True)
elif comp_status == 'different':
diff_dict = directory_diff(
source, target,
ignore_keys=self.keys_to_ignore, difference={}
)
diff_dict.update(parce_acl(source, target))
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
source_schema=source['schema'],
diff_schema=target_schema,
data=diff_dict)
return diff
SchemaDiffRegistry(blueprint.node_type, RuleView, 'table')
RuleView.register_node_view(blueprint)
@@ -11,365 +11,97 @@
import copy
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.utils.ajax import internal_server_error
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\
directory_diff
from pgadmin.tools.schema_diff.model import SchemaDiffModel
are_dictionaries_identical
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
class SchemaDiffTableCompare(SchemaDiffObjectCompare):
table_keys_to_ignore = ['oid', 'schema', 'edit_types', 'attnum',
'col_type', 'references', 'reltuples', 'oid-2',
'rows_cnt', 'seqrelid', 'atttypid', 'elemoid',
'hastoasttable', 'relhassubclass']
keys_to_ignore = ['oid', 'schema', 'vacuum_table',
'vacuum_toast', 'edit_types', 'attnum', 'col_type',
'references', 'reltuples', 'rows_cnt']
constraint_keys_to_ignore = ['relname', 'nspname', 'parent_tbl',
'attrelid', 'adrelid', 'fknsp', 'confrelid',
'references', 'refnsp', 'remote_schema',
'conkey', 'indkey']
keys_to_ignore_ddl_comp = ['oid',
'schema',
'columns',
'edit_types',
'primary_key',
'unique_constraint',
'exclude_constraint',
'check_constraint',
'foreign_key',
'reltuples',
'rows_cnt'
]
trigger_keys_to_ignore = ['xmin', 'tgrelid', 'tgfoid', 'tfunction',
'tgqual', 'tgconstraint']
index_keys_to_ignore = ['relowner', 'indrelid']
keys_to_remove = {
'columns': ['relname', 'nspname', 'parent_tbl', 'attrelid', 'adrelid'],
'primary_key': ['oid'],
'unique_constraint': ['oid'],
'check_constraint': ['oid', 'nspname'],
'foreign_key': ['oid', 'fknsp', 'confrelid'],
'exclude_constraint': ['oid'],
'partitions': ['oid'],
}
keys_to_remove_ddl_comp = {
'columns': ['relname', 'nspname', 'parent_tbl', 'attrelid', 'adrelid'],
'check_constraint': ['nspname'],
'foreign_key': ['fknsp', 'confrelid']
}
keys_to_ignore = table_keys_to_ignore + constraint_keys_to_ignore \
+ trigger_keys_to_ignore + index_keys_to_ignore
def compare(self, **kwargs):
"""
This function is used to compare all the table objects
from two different schemas.
:return: Comparison Dictionary
:param kwargs:
:return:
"""
src_sid = kwargs.get('source_sid')
src_did = kwargs.get('source_did')
src_scid = kwargs.get('source_scid')
tar_sid = kwargs.get('target_sid')
tar_did = kwargs.get('target_did')
tar_scid = kwargs.get('target_scid')
sub_modules = ['index', 'rule', 'trigger']
source_params = {'sid': kwargs.get('source_sid'),
'did': kwargs.get('source_did'),
'scid': kwargs.get('source_scid')}
target_params = {'sid': kwargs.get('target_sid'),
'did': kwargs.get('target_did'),
'scid': kwargs.get('target_scid')}
source_tables = self.fetch_tables(sid=src_sid, did=src_did,
scid=src_scid)
status, target_schema = self.get_schema(**target_params)
if not status:
return internal_server_error(errormsg=target_schema)
target_tables = self.fetch_tables(sid=tar_sid, did=tar_did,
scid=tar_scid)
if self.manager.version >= 120000:
sub_modules.append('compound_trigger')
source_tables = self.fetch_tables(**source_params)
target_tables = self.fetch_tables(**target_params)
# If both the dict have no items then return None.
if not (source_tables or target_tables) or (
len(source_tables) <= 0 and len(target_tables) <= 0):
return None
src_server_type, tar_server_type = self.get_server_type(src_sid,
tar_sid)
for module in sub_modules:
module_view = SchemaDiffRegistry.get_node_view(
module)
# Get sub module data for source tables
if module_view.blueprint.server_type is None or \
src_server_type in module_view.blueprint.server_type:
for key, val in source_tables.items():
source = module_view.fetch_objects_to_compare(
sid=src_sid,
did=src_did,
scid=src_scid,
tid=val['oid'],
oid=None,
ignore_keys=True
)
source_tables[key][module] = source
# Get sub module data for target tables
if module_view.blueprint.server_type is None or \
tar_server_type in module_view.blueprint.server_type:
for key, val in target_tables.items():
target = module_view.fetch_objects_to_compare(
sid=tar_sid,
did=tar_did,
scid=tar_scid,
tid=val['oid'],
oid=None,
ignore_keys=True
)
target_tables[key][module] = target
return compare_dictionaries(source_tables, target_tables,
return compare_dictionaries(self, source_params, target_params,
target_schema, source_tables,
target_tables,
self.node_type,
self.blueprint.COLLECTION_LABEL,
self.keys_to_ignore)
@staticmethod
def get_server_type(src_id, tar_id):
"""Get server types of source and target servers."""
driver = get_driver(PG_DEFAULT_DRIVER)
src_manager = driver.connection_manager(src_id)
tar_manager = driver.connection_manager(tar_id)
return src_manager.server_type, tar_manager.server_type
def ddl_compare(self, **kwargs):
"""
This function will compare properties of 2 tables and
return the source DDL, target DDL and Difference of them.
:param kwargs:
:return:
"""
source_params = {'sid': kwargs.get('source_sid'),
'did': kwargs.get('source_did'),
'scid': kwargs.get('source_scid'),
'tid': kwargs.get('source_oid'),
'json_resp': False
}
src_sid = kwargs.get('source_sid')
src_did = kwargs.get('source_did')
src_scid = kwargs.get('source_scid')
src_oid = kwargs.get('source_oid')
tar_sid = kwargs.get('target_sid')
tar_did = kwargs.get('target_did')
tar_scid = kwargs.get('target_scid')
tar_oid = kwargs.get('target_oid')
comp_status = kwargs.get('comp_status')
generate_script = False
target_params = {'sid': kwargs.get('target_sid'),
'did': kwargs.get('target_did'),
'scid': kwargs.get('target_scid'),
'tid': kwargs.get('target_oid'),
'json_resp': False
}
if 'generate_script' in kwargs and kwargs['generate_script']:
generate_script = True
source = ''
target = ''
diff = ''
ignore_sub_modules = ['column', 'constraints']
src_server_type, tar_server_type = self.get_server_type(src_sid,
tar_sid)
status, target_schema = self.get_schema(tar_sid,
tar_did,
tar_scid
)
if not status:
return internal_server_error(errormsg=target_schema)
if comp_status == SchemaDiffModel.COMPARISON_STATUS['source_only']:
if not generate_script:
source = self.get_sql_from_table_diff(sid=src_sid,
did=src_did,
scid=src_scid,
tid=src_oid,
json_resp=False)
diff = self.get_sql_from_table_diff(sid=src_sid, did=src_did,
scid=src_scid, tid=src_oid,
diff_schema=target_schema,
json_resp=False)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['target_only']:
if not generate_script:
target = self.get_sql_from_table_diff(sid=tar_sid,
did=tar_did,
scid=tar_scid,
tid=tar_oid,
json_resp=False)
diff = self.get_drop_sql(sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_oid)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['different']:
source = self.fetch_tables(
sid=src_sid, did=src_did,
scid=src_scid, tid=src_oid,
keys_to_remove=self.keys_to_remove_ddl_comp
)
target = self.fetch_tables(
sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_oid,
keys_to_remove=self.keys_to_remove_ddl_comp
)
if self.manager.version < 100000:
ignore_sub_modules.append('partition')
if self.manager.version < 120000:
ignore_sub_modules.append('compound_trigger')
# In case of error return None
if not (source or target):
return None
diff_dict = directory_diff(
source, target, ignore_keys=self.keys_to_ignore_ddl_comp,
difference={}
)
# Column comparison
col_diff = self.table_col_ddl_comp(source, target)
diff_dict.update(col_diff)
# Constraint comparison
pk_diff = self.constraint_ddl_comp(source, target, diff_dict)
diff_dict.update(pk_diff)
diff_dict.update(self.parce_acl(source, target))
if not generate_script:
source = self.get_sql_from_table_diff(sid=src_sid,
did=src_did,
scid=src_scid,
tid=src_oid,
json_resp=False)
target = self.get_sql_from_table_diff(sid=tar_sid,
did=tar_did,
scid=tar_scid,
tid=tar_oid,
json_resp=False)
diff = self.get_sql_from_table_diff(sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_oid,
diff_data=diff_dict,
json_resp=False)
for module in self.blueprint.submodules:
if module.NODE_TYPE not in ignore_sub_modules:
module_view = SchemaDiffRegistry.get_node_view(
module.NODE_TYPE)
if module_view.blueprint.server_type and (
src_server_type not in
module_view.blueprint.server_type and
tar_server_type not in
module_view.blueprint.server_type
):
continue
if module_view.blueprint.server_type and (
(src_server_type in
module_view.blueprint.server_type and
tar_server_type not in
module_view.blueprint.server_type) or (
src_server_type not in
module_view.blueprint.server_type and
tar_server_type in
module_view.blueprint.server_type)
):
continue
result = module_view.compare(
source_sid=src_sid, source_did=src_did,
source_scid=src_scid, source_tid=src_oid,
target_sid=tar_sid, target_did=tar_did,
target_scid=tar_scid, target_tid=tar_oid
)
if result and module.NODE_TYPE != 'partition':
child_diff = ''
for res in result:
if res['status'] == \
SchemaDiffModel.COMPARISON_STATUS[
'different']:
source_oid = res['source_oid']
target_oid = res['target_oid']
else:
source_oid = res['oid']
target_oid = res['oid']
if res['status'] != \
SchemaDiffModel.COMPARISON_STATUS[
'identical']:
child_diff = module_view.ddl_compare(
source_sid=src_sid, source_did=src_did,
source_scid=src_scid,
source_oid=source_oid,
source_tid=src_oid, target_sid=tar_sid,
target_did=tar_did, target_scid=tar_scid,
target_tid=tar_oid, target_oid=target_oid,
comp_status=res['status']
)
if child_diff:
diff += '\n' + child_diff
elif result:
# For partition module
identical = False
source_only = False
target_only = False
different = False
for res in result:
if res['status'] == \
SchemaDiffModel.COMPARISON_STATUS[
'identical']:
identical = True
elif res['status'] == \
SchemaDiffModel.COMPARISON_STATUS[
'source_only']:
source_only = True
elif res['status'] == \
SchemaDiffModel.COMPARISON_STATUS[
'target_only']:
target_only = True
else:
different = True
if identical:
pass
elif (source_only or target_only) and not different:
for res in result:
source_oid = res['oid']
target_oid = res['oid']
child_diff = module_view.ddl_compare(
source_sid=src_sid, source_did=src_did,
source_scid=src_scid,
source_oid=source_oid,
source_tid=src_oid, target_sid=tar_sid,
target_did=tar_did, target_scid=tar_scid,
target_tid=tar_oid, target_oid=target_oid,
comp_status=res['status']
)
if child_diff:
diff += child_diff
else:
diff = self.get_sql_from_table_diff(
sid=src_sid,
did=src_did,
scid=src_scid,
tid=src_oid,
diff_schema=target_schema,
json_resp=False,
schema_diff_table=True
)
else:
source = self.get_sql_from_table_diff(sid=src_sid, did=src_did,
scid=src_scid, tid=src_oid,
json_resp=False)
target = self.get_sql_from_table_diff(sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_oid,
json_resp=False)
source = self.get_sql_from_table_diff(**source_params)
target = self.get_sql_from_table_diff(**target_params)
return {'source_ddl': source,
'target_ddl': target,
'diff_ddl': diff
'diff_ddl': ''
}
@staticmethod
def table_col_ddl_comp(source, target):
def table_col_comp(source, target):
"""
Table Column comparison
:param source: Source columns
@@ -413,17 +145,14 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
return different
@staticmethod
def constraint_ddl_comp(source_table, target_table, diff_dict):
def table_constraint_comp(source_table, target_table):
"""
Table Constraint DDL comparison
:param source: Source Table
:param target: Target Table
:param source_table: Source Table
:param target_table: Target Table
:return: Difference of constraints
"""
different = {}
non_editable_keys = {}
columns_to_be_dropped = []
non_editable_keys = {'primary_key': ['col_count',
'condeferrable',
'condeffered',
@@ -456,14 +185,16 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
if type(target_cols) is list and len(
target_cols) > 0:
tmp_src = copy.deepcopy(source)
tmp_src.pop('oid')
if 'oid' in tmp_src:
tmp_src.pop('oid')
tmp_tar = None
tmp = None
for item in target_cols:
if item['name'] == source['name']:
tmp_tar = copy.deepcopy(item)
tmp = copy.deepcopy(item)
tmp_tar.pop('oid')
if 'oid' in tmp_tar:
tmp_tar.pop('oid')
if tmp_tar and tmp_src != tmp_tar:
tmp_updated = copy.deepcopy(source)
for key in non_editable_keys[constraint]:
@@ -474,7 +205,8 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
tmp_updated = None
break
if tmp_updated:
tmp_updated['oid'] = tmp['oid']
if 'oid' in tmp:
tmp_updated['oid'] = tmp['oid']
updated.append(tmp_updated)
target_cols.remove(tmp)
elif tmp_tar and tmp_src == tmp_tar:
@@ -492,18 +224,109 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
return different
def remove_keys_for_comparision(self, data, keys=None):
def get_sql_from_submodule_diff(self, source_params, target_params,
target_schema, source, target, diff_dict):
"""
This function is used to remove specific keys from data
This function returns the DDL/DML statements of the
submodules of table based on the comparison status.
:param source_params:
:param target_params:
:param target_schema:
:param source:
:param target:
:param diff_dict:
:return:
"""
# Get the difference result for source and target columns
col_diff = self.table_col_comp(source, target)
diff_dict.update(col_diff)
keys_to_remove = keys if keys else self.keys_to_remove
# Get the difference result for source and target constraints
pk_diff = self.table_constraint_comp(source, target)
diff_dict.update(pk_diff)
for p_key, p_val in keys_to_remove.items():
if p_key in data and data[p_key] is not None \
and len(data[p_key]) > 0:
for item in data[p_key]:
# Remove keys that should not be the part of comparision.
for key in p_val:
if key in item:
item.pop(key)
# Get the difference DDL/DML statements for table
target_params['diff_data'] = diff_dict
diff = self.get_sql_from_table_diff(**target_params)
ignore_sub_modules = ['column', 'constraints']
if self.manager.version < 100000:
ignore_sub_modules.append('partition')
if self.manager.server_type == 'pg' or self.manager.version < 120000:
ignore_sub_modules.append('compound_trigger')
# Iterate through all the sub modules of the table
for module in self.blueprint.submodules:
if module.NODE_TYPE not in ignore_sub_modules:
module_view = \
SchemaDiffRegistry.get_node_view(module.NODE_TYPE)
if module.NODE_TYPE == 'partition' and \
('is_partitioned' in source and source['is_partitioned'])\
and ('is_partitioned' in target and
target['is_partitioned']):
target_ddl = module_view.ddl_compare(
target_params=target_params,
parent_source_data=source,
parent_target_data=target
)
diff += '\n' + target_ddl
elif module.NODE_TYPE != 'partition':
dict1 = copy.deepcopy(source[module.NODE_TYPE])
dict2 = copy.deepcopy(target[module.NODE_TYPE])
# Find the duplicate keys in both the dictionaries
dict1_keys = set(dict1.keys())
dict2_keys = set(dict2.keys())
intersect_keys = dict1_keys.intersection(dict2_keys)
# Keys that are available in source and missing in target.
added = dict1_keys - dict2_keys
for item in added:
source_ddl = module_view.ddl_compare(
source_params=source_params,
target_params=target_params,
source=dict1[item],
target=None,
target_schema=target_schema,
comp_status='source_only'
)
diff += '\n' + source_ddl
# Keys that are available in target and missing in source.
removed = dict2_keys - dict1_keys
for item in removed:
target_ddl = module_view.ddl_compare(
source_params=source_params,
target_params=target_params,
source=None,
target=dict2[item],
target_schema=target_schema,
comp_status='target_only'
)
diff += '\n' + target_ddl
# Keys that are available in both source and target.
for key in intersect_keys:
# Recursively Compare the two dictionary
if not are_dictionaries_identical(
dict1[key], dict2[key], self.keys_to_ignore):
diff_ddl = module_view.ddl_compare(
source_params=source_params,
target_params=target_params,
source=dict1[key],
target=dict2[key],
target_schema=target_schema,
comp_status='different',
parent_source_data=source,
parent_target_data=target
)
diff += '\n' + diff_ddl
return diff
@@ -82,23 +82,23 @@ define('pgadmin.node.table', [
},{
name: 'truncate_table', node: 'table', module: this,
applies: ['object', 'context'], callback: 'truncate_table',
category: 'Truncate', priority: 3, label: gettext('Truncate'),
category: gettext('Truncate'), priority: 3, label: gettext('Truncate'),
icon: 'fa fa-eraser', enable : 'canCreate',
},{
name: 'truncate_table_cascade', node: 'table', module: this,
applies: ['object', 'context'], callback: 'truncate_table_cascade',
category: 'Truncate', priority: 3, label: gettext('Truncate Cascade'),
category: gettext('Truncate'), priority: 3, label: gettext('Truncate Cascade'),
icon: 'fa fa-eraser', enable : 'canCreate',
},{
// To enable/disable all triggers for the table
name: 'enable_all_triggers', node: 'table', module: this,
applies: ['object', 'context'], callback: 'enable_triggers_on_table',
category: 'Trigger(s)', priority: 4, label: gettext('Enable All'),
category: gettext('Trigger(s)'), priority: 4, label: gettext('Enable All'),
icon: 'fa fa-check', enable : 'canCreate_with_trigger_enable',
},{
name: 'disable_all_triggers', node: 'table', module: this,
applies: ['object', 'context'], callback: 'disable_triggers_on_table',
category: 'Trigger(s)', priority: 4, label: gettext('Disable All'),
category: gettext('Trigger(s)'), priority: 4, label: gettext('Disable All'),
icon: 'fa fa-times', enable : 'canCreate_with_trigger_disable',
},{
name: 'reset_table_stats', node: 'table', module: this,
@@ -9,7 +9,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
{% endif %}
{### Alter column type and collation ###}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname)%}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname) or data.col_type_conversion is defined %}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %}
-- WARNING:
-- The SQL statement below would normally be used to alter the datatype for the {{o_data.name}} column, however,
@@ -101,7 +101,7 @@ COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, o_data.name)}}
{% endif %}
{### Update column variables ###}
{% if 'attoptions' in data and data.attoptions and data.attoptions|length > 0 %}
{% if 'attoptions' in data and data.attoptions != None and data.attoptions|length > 0 %}
{% set variables = data.attoptions %}
{% if 'deleted' in variables and variables.deleted|length > 0 %}
ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
@@ -6,17 +6,18 @@
{% if data.name and data.name != o_data.name %}
ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
RENAME {{conn|qtIdent(o_data.name)}} TO {{conn|qtIdent(data.name)}};
{% endif %}
{### Alter column type and collation ###}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname) or data.col_type_conversion is defined %}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %}
-- WARNING:
-- The SQL statement below would normally be used to alter the datatype for the {{o_data.name}} column, however,
-- the current datatype cannot be cast to the target datatype so this conversion cannot be made automatically.
{% endif %}
{### Alter column type and collation ###}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname)%}
ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} TYPE {{ GET_TYPE.UPDATE_TYPE_SQL(conn, data, o_data) }}{% if data.collspcname and data.collspcname != o_data.collspcname %}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %} -- {% endif %}ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %} -- {% endif %} ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} TYPE {{ GET_TYPE.UPDATE_TYPE_SQL(conn, data, o_data) }}{% if data.collspcname and data.collspcname != o_data.collspcname %}
COLLATE {{data.collspcname}}{% elif o_data.collspcname %} COLLATE {{o_data.collspcname}}{% endif %};
{% endif %}
{### Alter column default value ###}
@@ -9,7 +9,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
{% endif %}
{### Alter column type and collation ###}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname)%}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname) or data.col_type_conversion is defined %}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %}
-- WARNING:
-- The SQL statement below would normally be used to alter the datatype for the XXX column, however,
@@ -0,0 +1,22 @@
CREATE TABLE {{conn|qtIdent(data.schema, data.name)}} (
LIKE {{conn|qtIdent(data.schema, data.orig_name)}} INCLUDING ALL
) PARTITION BY {{ data.partition_scheme }};
{{partition_sql}}
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %} {{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
{% if partition_data.partitions and partition_data.partitions|length > 0 %}
{% for part in partition_data.partitions %}
DROP TABLE IF EXISTS {{conn|qtIdent(data.schema, part.partition_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, part.temp_partition_name)}}
RENAME TO {{conn|qtIdent(part.partition_name)}};
{% endfor %}{% endif %}
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{data.orig_name}};
@@ -0,0 +1,24 @@
CREATE TABLE {{conn|qtIdent(data.schema, data.name)}} (
LIKE {{conn|qtIdent(data.schema, data.orig_name)}} INCLUDING ALL
) PARTITION BY {{ data.partition_scheme }};
{{partition_sql}}{{partition_data.default_partition_header}}
CREATE TABLE IF NOT EXISTS {{conn|qtIdent(data.schema, data.default_partition_name)}} PARTITION OF {{conn|qtIdent(data.schema, data.name)}} DEFAULT;
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %} {{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
{% if partition_data.partitions and partition_data.partitions|length > 0 %}
{% for part in partition_data.partitions %}
DROP TABLE IF EXISTS {{conn|qtIdent(data.schema, part.partition_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, part.temp_partition_name)}}
RENAME TO {{conn|qtIdent(part.partition_name)}};
{% endfor %}{% endif %}
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{data.orig_name}};
@@ -0,0 +1,22 @@
CREATE TABLE {{conn|qtIdent(data.schema, data.name)}} (
LIKE {{conn|qtIdent(data.schema, data.orig_name)}} INCLUDING ALL
) PARTITION BY {{ data.partition_scheme }};
{{partition_sql}}
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %} {{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
{% if partition_data.partitions and partition_data.partitions|length > 0 %}
{% for part in partition_data.partitions %}
DROP TABLE IF EXISTS {{conn|qtIdent(data.schema, part.partition_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, part.temp_partition_name)}}
RENAME TO {{conn|qtIdent(part.partition_name)}};
{% endfor %}{% endif %}
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{data.orig_name}};
@@ -0,0 +1,24 @@
CREATE TABLE {{conn|qtIdent(data.schema, data.name)}} (
LIKE {{conn|qtIdent(data.schema, data.orig_name)}} INCLUDING ALL
) PARTITION BY {{ data.partition_scheme }};
{{partition_sql}}{{partition_data.default_partition_header}}
CREATE TABLE IF NOT EXISTS {{conn|qtIdent(data.schema, data.default_partition_name)}} PARTITION OF {{conn|qtIdent(data.schema, data.name)}} DEFAULT;
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %} {{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
{% if partition_data.partitions and partition_data.partitions|length > 0 %}
{% for part in partition_data.partitions %}
DROP TABLE IF EXISTS {{conn|qtIdent(data.schema, part.partition_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, part.temp_partition_name)}}
RENAME TO {{conn|qtIdent(part.partition_name)}};
{% endfor %}{% endif %}
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{data.orig_name}};
@@ -1,14 +0,0 @@
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
{{partition_sql}}
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{conn|qtIdent(data.orig_name)}};
@@ -56,7 +56,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
{% if data.fillfactor and data.fillfactor != o_data.fillfactor %}
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
SET (FILLFACTOR={{data.fillfactor}});
{% elif data.fillfactor == '' and data.fillfactor != o_data.fillfactor %}
{% elif (data.fillfactor == '' or data.fillfactor == None) and data.fillfactor != o_data.fillfactor %}
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RESET (FILLFACTOR);
@@ -64,7 +64,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
{% if data.fillfactor and data.fillfactor != o_data.fillfactor %}
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
SET (FILLFACTOR={{data.fillfactor}});
{% elif data.fillfactor == '' and data.fillfactor != o_data.fillfactor %}
{% elif (data.fillfactor == '' or data.fillfactor == None) and data.fillfactor != o_data.fillfactor %}
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RESET (FILLFACTOR);
@@ -3,9 +3,13 @@ ALTER TRIGGER {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.nspname,
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
{% if ((data.prosrc is defined or data.is_row_trigger is defined or data.evnt_insert is defined or data.evnt_delete is defined or data.evnt_update is defined or data.fires is defined) and o_data.lanname == 'edbspl' and (o_data.prosrc != data.prosrc or data.is_row_trigger != o_data.is_row_trigger or data.evnt_insert != o_data.evnt_insert or data.evnt_delete != o_data.evnt_delete or data.evnt_update != o_data.evnt_update or o_data.fires != data.fires)) %}
{% if ((data.prosrc is defined or data.is_row_trigger is defined or data.evnt_insert is defined or data.evnt_delete is defined or data.evnt_update is defined or data.fires is defined or data.is_constraint_trigger is defined) and (o_data.prosrc != data.prosrc or data.is_row_trigger != o_data.is_row_trigger or data.evnt_insert != o_data.evnt_insert or data.evnt_delete != o_data.evnt_delete or data.evnt_update != o_data.evnt_update or o_data.fires != data.fires or data.is_constraint_trigger != o_data.is_constraint_trigger)) %}
{% set or_flag = False %}
{% if data.lanname == 'edbspl' or data.tfunction == 'Inline EDB-SPL' %}
CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }}
{% else %}
CREATE{% if data.is_constraint_trigger %} CONSTRAINT{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}
{% endif %}
{% if data.fires is defined %}{{data.fires}} {% else %}{{o_data.fires}} {% endif %}{% if data.evnt_insert is not defined %}{% if o_data.evnt_insert %}INSERT{% set or_flag = True %}
{% endif %}{% else %}{% if data.evnt_insert %}INSERT{% set or_flag = True %}{% endif %}{% endif %}{% if data.evnt_delete is not defined %}{% if o_data.evnt_delete %}
{% if or_flag %} OR {% endif %}DELETE{% set or_flag = True %}
@@ -16,23 +20,29 @@ CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }}
{% if or_flag %} OR {% endif %}TRUNCATE{% set or_flag = True %}{%endif %}{% endif %}{% if data.evnt_update is not defined %}{% if o_data.evnt_update %}
{% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}
{% endif %}{% else %}{% if data.evnt_update %}
{% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}{% endif %}
{% if or_flag %} OR {% endif %}UPDATE {% if data.columns|length > 0 %}OF {% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}{% endif %}
{% endif %}
ON {{ conn|qtIdent(data.schema, data.table) }}
{% if o_data.tgdeferrable %}
{% if data.tgdeferrable %}
DEFERRABLE{% if data.tginitdeferred %} INITIALLY DEFERRED{% endif %}
{% elif o_data.tgdeferrable %}
DEFERRABLE{% if o_data.tginitdeferred %} INITIALLY DEFERRED{% endif %}
{% endif %}{% if data.is_row_trigger is not defined %}
FOR EACH{% if o_data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% else %}
FOR EACH{% if data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% endif %}
{% if o_data.whenclause %}
{% if data.whenclause %}
WHEN {{ data.whenclause }}
{% elif o_data.whenclause %}
WHEN {{ o_data.whenclause }}
{% endif %}
{% if (data.prosrc is not defined) %}
{{ o_data.prosrc }};
{% if (data.tfunction is defined) %}
EXECUTE PROCEDURE {{ data.tfunction }}{% if data.tgargs %}({{ data.tgargs }}){% else %}(){% endif%};
{% else %}
{{ data.prosrc }};
EXECUTE PROCEDURE {{ o_data.tfunction }}{% if o_data.tgargs %}({{ o_data.tgargs }}){% else %}(){% endif%};
{% endif %}
{% if data.description is not defined and o_data.description %}
@@ -3,9 +3,13 @@ ALTER TRIGGER {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.nspname,
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
{% if ((data.prosrc is defined or data.is_row_trigger is defined or data.evnt_insert is defined or data.evnt_delete is defined or data.evnt_update is defined or data.fires is defined) and o_data.lanname == 'edbspl' and (o_data.prosrc != data.prosrc or data.is_row_trigger != o_data.is_row_trigger or data.evnt_insert != o_data.evnt_insert or data.evnt_delete != o_data.evnt_delete or data.evnt_update != o_data.evnt_update or o_data.fires != data.fires)) %}
{% if ((data.prosrc is defined or data.is_row_trigger is defined or data.evnt_insert is defined or data.evnt_delete is defined or data.evnt_update is defined or data.fires is defined or data.is_constraint_trigger is defined) and (o_data.prosrc != data.prosrc or data.is_row_trigger != o_data.is_row_trigger or data.evnt_insert != o_data.evnt_insert or data.evnt_delete != o_data.evnt_delete or data.evnt_update != o_data.evnt_update or o_data.fires != data.fires or data.is_constraint_trigger != o_data.is_constraint_trigger)) %}
{% set or_flag = False %}
{% if data.lanname == 'edbspl' or data.tfunction == 'Inline EDB-SPL' %}
CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }}
{% else %}
CREATE{% if data.is_constraint_trigger %} CONSTRAINT{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}
{% endif %}
{% if data.fires is defined %}{{data.fires}} {% else %}{{o_data.fires}} {% endif %}{% if data.evnt_insert is not defined %}{% if o_data.evnt_insert %}INSERT{% set or_flag = True %}
{% endif %}{% else %}{% if data.evnt_insert %}INSERT{% set or_flag = True %}{% endif %}{% endif %}{% if data.evnt_delete is not defined %}{% if o_data.evnt_delete %}
{% if or_flag %} OR {% endif %}DELETE{% set or_flag = True %}
@@ -16,24 +20,39 @@ CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }}
{% if or_flag %} OR {% endif %}TRUNCATE{% set or_flag = True %}{%endif %}{% endif %}{% if data.evnt_update is not defined %}{% if o_data.evnt_update %}
{% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}
{% endif %}{% else %}{% if data.evnt_update %}
{% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}{% endif %}
{% if or_flag %} OR {% endif %}UPDATE {% if data.columns|length > 0 %}OF {% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}{% endif %}
{% endif %}
ON {{ conn|qtIdent(data.schema, data.table) }}
{% if o_data.tgdeferrable %}
{% if data.tgdeferrable %}
DEFERRABLE{% if data.tginitdeferred %} INITIALLY DEFERRED{% endif %}
{% elif o_data.tgdeferrable %}
DEFERRABLE{% if o_data.tginitdeferred %} INITIALLY DEFERRED{% endif %}
{% endif %}{% if data.is_row_trigger is not defined %}
FOR EACH{% if o_data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% else %}
FOR EACH{% if data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% endif %}
{% if o_data.whenclause %}
{% if data.whenclause %}
WHEN {{ data.whenclause }}
{% elif o_data.whenclause %}
WHEN {{ o_data.whenclause }}
{% endif %}
{%if data.tfunction == 'Inline EDB-SPL' %}
{% if (data.prosrc is not defined) %}
{{ o_data.prosrc }};
{% else %}
{{ data.prosrc }};
{% endif %}
{% else %}
{% if (data.tfunction is defined) %}
EXECUTE PROCEDURE {{ data.tfunction }}{% if data.tgargs %}({{ data.tgargs }}){% else %}(){% endif%};
{% else %}
EXECUTE PROCEDURE {{ o_data.tfunction }}{% if o_data.tgargs %}({{ o_data.tgargs }}){% else %}(){% endif%};
{% endif %}
{% endif %}
{% if data.description is not defined and o_data.description %}
COMMENT ON TRIGGER {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
@@ -29,6 +29,8 @@ from pgadmin.utils.compile_template_name import compile_template_path
from pgadmin.utils import IS_PY2
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.tools.schema_diff.directory_compare import directory_diff,\
parce_acl
# If we are in Python3
if not IS_PY2:
@@ -249,7 +251,8 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
})
# Schema Diff: Keys to ignore while comparing
keys_to_ignore = ['oid', 'xmin', 'nspname', 'tgrelid', 'tgfoid', 'prosrc']
keys_to_ignore = ['oid', 'xmin', 'nspname', 'tgrelid', 'tgfoid', 'prosrc',
'oid-2']
def check_precondition(f):
"""
@@ -794,7 +797,7 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
SQL, name = trigger_utils.get_sql(
self.conn, data, tid, oid,
self.datlastsysoid,
self.blueprint.show_system_objects)
self.blueprint.show_system_objects, True)
if not isinstance(SQL, (str, unicode)):
return SQL
@@ -932,8 +935,7 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
)
@check_precondition
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None,
ignore_keys=False):
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None):
"""
This function will fetch the list of all the triggers for
specified schema id.
@@ -963,14 +965,59 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
for row in triggers['rows']:
status, data = self._fetch_properties(tid, row['oid'])
if status:
if ignore_keys:
for key in self.keys_to_ignore:
if key in data:
del data[key]
res[row['name']] = data
return res
def ddl_compare(self, **kwargs):
"""
This function returns the DDL/DML statements based on the
comparison status.
:param kwargs:
:return:
"""
src_params = kwargs.get('source_params')
tgt_params = kwargs.get('target_params')
source = kwargs.get('source')
target = kwargs.get('target')
target_schema = kwargs.get('target_schema')
comp_status = kwargs.get('comp_status')
diff = ''
if comp_status == 'source_only':
diff = self.get_sql_from_diff(gid=src_params['gid'],
sid=src_params['sid'],
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
oid=source['oid'],
diff_schema=target_schema)
elif comp_status == 'target_only':
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
drop_sql=True)
elif comp_status == 'different':
diff_dict = directory_diff(
source, target,
ignore_keys=self.keys_to_ignore, difference={}
)
diff_dict.update(parce_acl(source, target))
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
data=diff_dict)
return diff
SchemaDiffRegistry(blueprint.node_type, TriggerView, 'table')
TriggerView.register_node_view(blueprint)
@@ -141,7 +141,7 @@ def get_trigger_function_and_columns(conn, data, tid,
@get_template_path
def get_sql(conn, data, tid, trid, datlastsysoid,
show_system_objects, template_path=None):
show_system_objects, is_schema_diff=False, template_path=None):
"""
This function will generate sql from model data.
@@ -151,6 +151,7 @@ def get_sql(conn, data, tid, trid, datlastsysoid,
:param trid: Trigger ID
:param datlastsysoid:
:param show_system_objects: Show System Object value True or False
:param is_schema_diff:
:param template_path: Optional template path
:return:
"""
@@ -173,6 +174,35 @@ def get_sql(conn, data, tid, trid, datlastsysoid,
if 'name' not in data:
name = data['name'] = old_data['name']
drop_sql = ''
if is_schema_diff:
if 'table' not in data:
data['table'] = old_data['relname']
if 'schema' not in data:
data['schema'] = old_data['nspname']
# If any of the below key is present in data then we need to drop
# trigger and re-create it.
key_array = ['prosrc', 'is_row_trigger', 'evnt_insert',
'evnt_delete', 'evnt_update', 'fires', 'tgdeferrable',
'whenclause', 'tfunction', 'tgargs', 'columns',
'is_constraint_trigger', 'tginitdeferred']
is_drop_trigger = False
for key in key_array:
if key in data:
is_drop_trigger = True
break
if is_drop_trigger:
tmp_data = dict()
tmp_data['name'] = data['name']
tmp_data['nspname'] = old_data['nspname']
tmp_data['relname'] = old_data['relname']
drop_sql = render_template("/".join([template_path,
'delete.sql']),
data=tmp_data, conn=conn)
old_data = get_trigger_function_and_columns(
conn, old_data, tid, show_system_objects)
@@ -182,6 +212,9 @@ def get_sql(conn, data, tid, trid, datlastsysoid,
"/".join([template_path, 'update.sql']),
data=data, o_data=old_data, conn=conn
)
if is_schema_diff:
SQL = drop_sql + '\n' + SQL
else:
required_args = {
'name': 'Name',
@@ -588,6 +588,10 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
if not json_resp:
display_comments = False
res_data = parse_rule_definition(res)
# Update the correct table name for rules
if 'view' in res_data:
res_data['view'] = table
rules_sql += render_template("/".join(
[self.rules_template_path, 'create.sql']),
data=res_data, display_comments=display_comments)
@@ -1298,11 +1302,12 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
status=200
)
def get_partitions_sql(self, partitions):
def get_partitions_sql(self, partitions, schema_diff=False):
"""
This function will iterate all the partitions and create SQL.
:param partitions: List of partitions
:param schema_diff: If true then create sql accordingly.
"""
sql = ''
@@ -1361,7 +1366,10 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
# Check if partition is again declare as partitioned table.
if 'is_sub_partitioned' in row and row['is_sub_partitioned']:
part_data['partition_scheme'] = self.get_partition_scheme(row)
part_data['partition_scheme'] = row['sub_partition_scheme'] \
if 'sub_partition_scheme' in row else \
self.get_partition_scheme(row)
part_data['is_partitioned'] = True
if 'is_attach' in row and row['is_attach']:
@@ -1370,12 +1378,17 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
data=part_data, conn=self.conn
)
else:
# For schema diff we create temporary partitions to copy the
# data from original table to temporary table.
if schema_diff:
part_data['name'] = row['temp_partition_name']
partition_sql = render_template(
"/".join([self.partition_template_path, 'create.sql']),
data=part_data, conn=self.conn
)
sql += partition_sql + '\n'
sql += partition_sql
return sql
@@ -219,6 +219,9 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
{'get': 'get_external_functions_list'}]
})
keys_to_ignore = ['oid', 'typnamespace', 'typrelid', 'typarray', 'alias',
'schema', 'oid-2']
def check_precondition(f):
"""
This function will behave as a decorator which will checks
@@ -1071,7 +1074,7 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
return internal_server_error(errormsg=str(e))
@check_precondition
def delete(self, gid, sid, did, scid, tid=None):
def delete(self, gid, sid, did, scid, tid=None, only_sql=False):
"""
This function will updates existing the type object
@@ -1081,6 +1084,7 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
did: Database ID
scid: Schema ID
tid: Type ID
only_sql: Return only sql if True
"""
if tid is None:
data = request.form if request.form else json.loads(
@@ -1090,7 +1094,7 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
data = {'ids': [tid]}
# Below will decide if it's simple drop or drop with cascade call
if self.cmd == 'delete':
if self.cmd == 'delete' or only_sql:
# This is a cascade operation
cascade = True
else:
@@ -1128,6 +1132,11 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
data=data,
cascade=cascade,
conn=self.conn)
# Used for schema diff tool
if only_sql:
return SQL
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
@@ -1279,10 +1288,19 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
old_data.update(self.additional_properties(old_data, tid))
old_data = self._convert_for_sql(old_data)
SQL = render_template(
"/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data, conn=self.conn
)
# If typname or collname is changed while comparing
# two schemas then we need to drop type and recreate it
if 'typtype' in data or 'typname' in data or 'collname' in data\
or 'typinput' in data or 'typoutput' in data:
SQL = render_template(
"/".join([self.template_path, 'type_schema_diff.sql']),
data=data, o_data=old_data, conn=self.conn
)
else:
SQL = render_template(
"/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data, conn=self.conn
)
else:
required_args = [
'name',
@@ -1331,7 +1349,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
return SQL, data['name'] if 'name' in data else old_data['name']
@check_precondition
def sql(self, gid, sid, did, scid, tid):
def sql(self, gid, sid, did, scid, tid, diff_schema=None,
json_resp=True):
"""
This function will generates reverse engineered sql for type object
@@ -1341,6 +1360,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
did: Database ID
scid: Schema ID
tid: Type ID
diff_schema: Target Schema for schema diff
json_resp: True then return json response
"""
SQL = render_template(
"/".join([self.template_path,
@@ -1359,6 +1380,9 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
# Making copy of output for future use
data = dict(res['rows'][0])
if diff_schema:
data['schema'] = diff_schema
SQL = render_template("/".join([self.template_path, 'acl.sql']),
scid=scid, tid=tid)
status, acl = self.conn.execute_dict(SQL)
@@ -1401,6 +1425,9 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
data=data, conn=self.conn)
SQL = sql_header + '\n\n' + SQL
if not json_resp:
return SQL.strip('\n')
return ajax_response(response=SQL)
@check_precondition
@@ -1473,5 +1500,38 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
return res
def get_sql_from_diff(self, gid, sid, did, scid, oid, data=None,
diff_schema=None, drop_sql=False):
"""
This function is used to get the DDL/DML statements.
:param gid: Group ID
:param sid: Serve ID
:param did: Database ID
:param scid: Schema ID
:param oid: Collation ID
:param data: Difference data
:param diff_schema: Target Schema
:param drop_sql: True if need to drop the types
:return:
"""
sql = ''
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, scid=scid,
data=data, tid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, tid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, TypeView)
TypeView.register_node_view(blueprint)
@@ -26,12 +26,14 @@ FROM pg_enum
{# The SQL given below will fetch range type#}
{% if type == 'r' %}
SELECT rngsubtype, st.typname,
rngcollation, col.collname,
rngcollation,
CASE WHEN n.nspname IS NOT NULL THEN concat(quote_ident(n.nspname), '.', quote_ident(col.collname)) ELSE col.collname END AS collname,
rngsubopc, opc.opcname,
rngcanonical, rngsubdiff
FROM pg_range
LEFT JOIN pg_type st ON st.oid=rngsubtype
LEFT JOIN pg_collation col ON col.oid=rngcollation
LEFT JOIN pg_namespace n ON col.collnamespace=n.oid
LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
WHERE rngtypid={{tid}}::oid;
{% endif %}
Loaded 100 of 194 files, more files were not shown because too many files have changed in this diff. Show more