diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..f416309e1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# https://editorconfig.org/ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{php,css,js,xml}] +indent_style = tab + +[*.json] +indent_style = space +indent_size = 4 + +[*.md] +indent_style = space +trim_trailing_whitespace = false +max_line_length = 120 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..83e7eeb35 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +/.gitattributes export-ignore +/.github export-ignore +/.gitignore export-ignore +/.gitmodules export-ignore +/.travis.yml export-ignore +/tests export-ignore diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index f515c64a7..ec0cf0978 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,3 @@ +github: vrana patreon: jakubvrana -custom: ["https://sourceforge.net/p/adminer/donate/"] +custom: ["https://www.paypal.com/donate/?hosted_button_id=6PK5VNUCFT3FG"] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..a3ffceb4c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,20 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Adminer version:** +**Compiled:** single file / single language / source codes / custom compilation +**Driver:** +**Database version:** +**Plugins used:** + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..113ec42be --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,19 @@ +name: CI + +on: + pull_request: + branches: [ master ] + workflow_dispatch: + +jobs: + build-test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: php-actions/composer@v6 + - uses: php-actions/phpcs@v1 + with: + path: . + standard: phpcs.xml + - uses: php-actions/phpstan@v3 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index b681ea31f..000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,67 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ master ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master ] - schedule: - - cron: '20 16 * * 0' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - language: [ 'javascript' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.gitignore b/.gitignore index 2afb1ac59..70f488189 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,12 @@ /adminer/adminer.css +/adminer/adminer-dark.css +/editor/adminer.css +/editor/adminer-dark.css /adminer*.php /editor*.php +/tests/pdo-*.html +/tests/screenshots/ +/tests/cropped/ /vendor/ +adminer-plugins/ +adminer-plugins.php diff --git a/.gitmodules b/.gitmodules index 5810a5e7a..04841dce1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,9 @@ [submodule "jush"] path = externals/jush - url = git://github.com/vrana/jush + url = https://github.com/vrana/jush [submodule "JsShrink"] path = externals/JsShrink - url = git://github.com/vrana/JsShrink -[submodule "designs/hydra"] - path = designs/hydra - url = https://github.com/Niyko/Hydra-Dark-Theme-for-Adminer -[submodule "designs/pepa-linha-dark"] - path = designs/pepa-linha-dark - url = https://github.com/pepa-linha/Adminer-Design-Dark/ + url = https://github.com/vrana/JsShrink +[submodule "PhpShrink"] + path = externals/PhpShrink + url = https://github.com/vrana/PhpShrink diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 937ea7c75..000000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: php -php: - - 5.6 - - 7.1 - - 7.2 - - 7.3 - - 7.4 - - 8.0 -script: git diff --name-only $TRAVIS_COMMIT_RANGE | grep '\.php$' | xargs -n1 -P8 php -l | grep -v 'No syntax errors'; test $? -eq 1 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..beb65d744 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1278 @@ +## Adminer 5.4.2 (released 2026-02-08) +- Avoid denial-of-service via version check (GHSA-q4f2-39gr-45jh, regression from 4.6.2) +- Pretty print JSON in edit +- Support multiline generated values in alter table +- Link //domain.tld values +- Improve print of nested tables +- Hide sort links on unsortable columns +- Display uneditable fields in edit form +- Shorten all but numeric and date types in select +- Fix escaping spaces in cookie value (bug #1208) +- Don't quote comma in TSV export (bug #1238) +- MariaDB: Don't display checks with the same name from another table (bug #1135) +- PostgreSQL: Offer foreign keys in create table +- PostgreSQL: Add missing parentheses to CHECK export +- PostgreSQL: Allow creating NOT DEFERRABLE foreign keys +- PostgreSQL: Remove duplicate DEFERRABLE in foreign key export +- PostgreSQL: Add schema to sequence and view export +- PostgreSQL: Fix definition of complex generated columns +- PostgreSQL: Mark unique partial indexes as unique (bug #1172) +- PostgreSQL: Fix namespace in inheritance links (bug #1221) +- non-PostgreSQL: Display NOT NULL checks (bug #1237) +- ClickHouse: Fix offset (bug #1188) +- ClickHouse: Fix list of tables (bug #1176) +- Plugins: Methods showVariables() and showStatus() (bug #1157) +- Plugins: Allow to be in any namespace +- New plugin: IGDB driver + +## Adminer 5.4.1 (released 2025-09-26) +- SQL command: Unlink NULL primary keys +- Do not quote 0 in CSV export +- Warn about exceeded upload_max_filesize in imports +- Prolong queries saved from SQL command to URL (bug #1154) +- MySQL: Fix displaying routine definition (bug #1156, regression from 5.4.0) + +## Adminer 5.4.0 (released 2025-09-08) +- Allow specifying operator in search anywhere +- Do not order descending in GROUP BY select +- Allow exporting SQL in SQL command (bug #1092) +- Add section links in database overview +- Warn about exceeded max_file_uploads in import +- Display @ after username without server in existing logins +- Display data length and index length for materialized views +- Link routines from syntax highlighting +- Autofocus added field in alter table +- Executed SQL commands: Add button for copy to clipboard +- Load more: run syntax highlighter +- Allow connecting to IPv6 (bug #1095) +- MySQL: Fix saving empty enum (bug #1152) +- MySQL 5.0-: Do not load partitioning info in alter table (bug #1099) +- MariaDB: Parse COLLATE in routine definition (bug #1104) +- PostgreSQL: Show structure of inherited tables +- PostgreSQL: Display index expressions +- PostgreSQL: Add SQL operator to select +- PostgreSQL: Hide only partitions, not all inherited tables from menu +- PostgreSQL: Allow comparing json columns (bug #1107) +- PostgreSQL: Shorten values in hstore columns +- PostgreSQL: Quote edit value with interval operator +- PostgreSQL: Fix calling functions with name-less parameters +- PostgreSQL: Fix calling functions returing table +- PostgreSQL: Don't treat user types containing 'file' as blobs (bug #1118) +- PostgreSQL: Export DROP and CREATE DATABASE (bug #1140) +- PostgreSQL 11-: Avoid duplicate oid in table status (bug #1089, regression from 5.3.0) +- Elasticsearch: Support dropping aliases +- Plugins: Methods afterConnect(), processList() and killProcess() +- New plugin: Display row numbers in select (bug #1106) +- New plugin: Specify query timeout + +## Adminer 5.3.0 (released 2025-05-04) +- Align numeric functions right +- Autocomplete: Support table aliases +- Fix type error in Create function (bug #1053, regression from 5.1.1) +- Add border to column actions (bug #1072) +- Align money values right (bug #1071) +- MySQL: Avoid warning on selecting tables with fulltext indexes (bug #1036) +- MySQL, PostgreSQL: Support index algorithms (bug #1030) +- MySQL: Fix connecting to localhost:3306 (bug #1057, regression from 5.1.1) +- PostgreSQL, CockroachDB: Creating partitioned tables (bug #1031) +- PostgreSQL: Move partitioned tables from table list to parent table +- PostgreSQL: Support partial indices (bug #1048) +- PostgreSQL: Support calling functions returning table (bug #1040) +- PostgreSQL: Add NOT ILIKE operator (bug #1066) +- Editor: Fix bit and enum search (bug #1062) +- Designs: adminer.css with 'prefers-color-scheme: dark' doesn't disable dark mode +- Plugins: Method bodyClass() to add <body class> +- Plugins: Allow setting dark mode in css() +- Hindi translation + +## Adminer 5.2.1 (released 2025-04-11) +- Fix search anywhere (bug #1004, regression from 5.1.1) +- Fix import without primary key (bug #1017, regression from 5.1.1) +- PostgreSQL PDO: Fix bytea without primary key (bug #1021) +- non-MySQL: Parse '--' without trailing space as comment in SQL command (bug #1025, regression from 5.2.0) + +## Adminer 5.2.0 (released 2025-04-08) +- Autocomplete SQL commands +- Do not edit NULL values by Modify (bug #967) +- Fix foreign key actions (regression from 5.1.1) +- MySQL: Display number of found rows in group queries (regression from 5.1.1) +- PostgreSQL: Support COPY FROM stdin in SQL query (bug #942) +- non-MySQL: Parse '--' without trailing space as comment in SQL command (bug SF-842) +- MS SQL: Limit one INSERT in export to 1000 rows (bug #983) +- CSS: Add logo +- Editor: Move mass sending e-mails to a plugin +- Plugins: Support translations by extending Adminer\Plugin +- New plugin: Configure options by end-users and store them to a cookie +- New plugin: Configure menu table links +- New plugin: Set up driver, server and database in Adminer Editor + +## Adminer 5.1.1 (released 2025-04-02) +- Export: Fix tar (regression from 5.0.3) +- Select: Allow ordering by COUNT(*) (bug #966, regression from 5.0.2) +- Optimize retrieving columns for schema +- Elasticsearch: Make it work with Elasticsearch 8 +- CSS: Hide menu on mobile +- CSS: Invert icons in dark mode +- Plugins: Allow changing CSP by more plugins +- New plugin: Use Monaco Editor for syntax highlighting +- New plugin: Use Prism for syntax highlighting + +## Adminer 5.1.0 (released 2025-03-24) +- Display collation at table structure if different from table +- Ctrl+click in select moves the cursor in modern browsers +- URL parameter ?ext=pdo to force using PDO +- PDO: Handle PHP warnings for internal queries +- PostgreSQL: Display auto_increment of inserted rows +- PostgreSQL: Display description of system variables +- PostgreSQL: Avoid warning about crdb_version (bug #924, regression from 5.0.5) +- PostgreSQL 11: Support PROCEDURE +- SQLite PDO: Display results in SQL query +- MS SQL: Fix collation issues when retrieving default values +- MS SQL PDO: Display last insert ID +- CSS: Sticky table headers (bug #918) +- CSS: Allow more custom styles with dark mode (bug #925) +- CSS: Increase maximum width of string edit (bug #930) +- CSS: Increase space after SQL result (bug #937) +- Plugins: Autoload plugins from adminer-plugins/ +- Plugins: Configure plugins with adminer-plugins.php +- Plugins: Display loaded plugins in server overview +- New plugin: AI prompt in SQL command generating the queries with Google Gemini +- New plugin: Verify new versions from GitHub +- New plugin: IMAP driver created for fun +- New plugin: Display links to tables referencing current row +- New plugin: Allow switching light and dark mode (bug #926) +- New plugin: Confirm before unloading page with changed form +- Uzbek translation + +## Adminer 5.0.6 (released 2025-03-17) +- Align numbers right (bug #912) +- Display comment in title of field +- Remember export setting at SQL command +- Shorten queries saved from SQL command to URL (bug #917) +- SQL textarea: Open help on Ctrl+click +- Security: Disallow writing temporary files to symlinks (bug SF-855) +- MariaDB: Display MariaDB instead of MySQL +- CSS: Dark mode syntax highlighting +- CSS: Dark input fields in dark mode +- Designs named adminer-dark.css use dark basic style +- Plugins: Add method syntaxHighlighting() +- New plugin: Use Codemirror 5 for syntax highlighting and SQL with typeahead + +## Adminer 5.0.5 (released 2025-03-13) +- MySQL: Display converting function for binary, bit or geometry fields +- MySQL: Display default values of binary columns +- MySQL: Allow setting default values of json column +- MariaDB: Don't display NULL as default value (regression from 5.0.0) +- PostgreSQL PDO: Escape bytea values (bug SF-218) +- CockroachDB: Display version +- CockroachDB: Recognize unique_rowid() as auto_increment +- MS SQL: Fix editing rows with datetime column in primary key +- MongoDB: Move to plugin +- CSS: Add dark theme + +## Adminer 5.0.4 (released 2025-03-11) +- Compile: Fix shortening in private methods (regression from 5.0.3) + +## Adminer 5.0.3 (released 2025-03-11) +- Fix gzip export (bug #896, regression from 5.0.0) +- Fix importing multiple SQL files not terminated by semicolon +- Use <datalist> for altering collations +- MySQL: Allow setting default values of text column +- MySQL: Stop treating enum and set as numbers (bug SF-475) +- MySQL, MariaDB: Fix default values with ' (bug #895) +- MariaDB: Fix creating and altering generated columns (bug #897) +- PostgreSQL: Fix "where" and "order" privileges (bug #902, regression from 5.0.2) +- SQLite: Fix creating table in compiled version (bug #901, regression from 5.0.0) +- Elasticsearch: Do not pass null values on insert (PR #892) +- Elasticsearch: Fix displaying sparse rows (PR #893) +- Plugins: Add method dumpFooter() + +## Adminer 5.0.2 (released 2025-03-10) +- PostgreSQL: Fix setting NULL and original value on enum (bug SF-884) +- CockroachDB: Add support via PostgreSQL driver +- Elasticsearch: Add support for "where" and "order" field privilege + +## Adminer 5.0.1 (released 2025-03-07) +- Fix bulk operations with tables (regression from 5.0.0) +- Remove duplicate columns from select (bug SF-670) +- MariaDB: Fix link to status variable doc (bug SF-658) +- PostgreSQL: Support indexes on materialized views (PR #467) +- Elasticsearch: Drop support for version < 7 + +## Adminer 5.0.0 (released 2025-03-07) +- Speed up with disabled output buffering +- Allow creating generated columns (bug SF-857) +- Don't autofocus computed fields in insert form +- Skip generated columns in multi-edit (bug SF-882) +- MySQL: Display generated value in table structure +- MySQL: Drop support for MySQL 4 +- PostgreSQL: Compute size of all databases (bug SF-881) +- PostgreSQL: Do not alter indexes with expressions +- PostgreSQL: Fix export of indexes with expressions (bug SF-768) +- PostgreSQL: Display ENUM types +- PostgreSQL: Export ENUM types (bug SF-587) +- PostgreSQL: Display ? instead of -1 rows in table overview (bug SF-883) +- PostgreSQL: Show accessible databases to non-owners (regression from 4.9.1) +- PostgreSQL: Skip editing generated columns +- PostgreSQL, MS SQL, Oracle: Hide table actions for information_schema +- SQLite: Support CHECK constraint +- SQLite: Support generated columns +- SQLite: Add command Check tables +- SQLite: Display all rows of variable values +- SQLite: Remove support for SQLite version 2 +- MS SQL: Support export (bug SF-480) +- MS SQL: Display foreign keys ON UPDATE and ON DELETE +- MS SQL: Support computed columns +- MS SQL: Fix CSV import (bug SF-859) +- MS SQL: Fix altering foreign key +- MS SQL PDO: Support offset +- MS SQL: Remove support for MSSQL extension +- MS SQL: Add support for PDO_SQLSRV extension +- MS SQL: Link help from sys tables +- MS SQL: Fix highlighting columns as primary keys +- MongoDB: Remove support for deprecated extension mongo +- Elasticsearch: Fix text search on boolean fields +- Plugins: Adminer code is now in a namespace + +## Adminer 4.17.1 (released 2025-02-25) +- MySQL: Fix typo in the date type (regression from 4.17.0) + +## Adminer 4.17.0 (released 2025-02-24) +- Hide index column options by default +- Offer original values in multi-row editing (regression from 4.16.0) +- Print SQL errors as comments in export (regression from 3.2.0) +- MySQL, PostgreSQL, MS SQL: Support CHECK constraint +- MySQL: Show comments at routine call (bug SF-874) +- MySQL: Don't offer empty enum value in edit +- MySQL 9+: Support vector type +- PostgreSQL: Link user defined types +- PostgreSQL: Constraint enum values in editing (bug SF-270) +- PostgreSQL: Export functions +- PostgreSQL 8+: Fix exporting table constraints +- SQLite: Show all supported pragmas in Variables +- MS SQL: Allow altering table in non-default schema (bug SF-405) +- MS SQL: Fix default values (bug SF-732, bug SF-733) +- MS SQL: Fix length of nvarchar columns +- Editor PDO: Select value of foreign key in edit (bug SF-847) +- Mobile devices: Use device width + +## Adminer 4.16.0 (released 2025-02-20) +- MySQL: Fix saving bit(64) values (bug SF-839) +- PostgreSQL: Preserve whitespace in EXPLAIN (bug SF-827) +- PostgreSQL: Support SSL +- PostgreSQL: Support altering auto_increment (bug SF-761) +- SQLite: Fix altering forign keys (bug SF-841) +- SQLite: Fix expressions in default values (bug SF-860) +- MS SQL: Foreign keys in non-default schema (bug SF-833) +- Oracle: Include tables granted by other user +- Elasticsearch: Move to plugin +- MongoDB: Execute commands against the selected DB + +## Adminer 4.15.0 +- Escape unknown field in select +- HTTP drivers: Don't allow path in server name +- HTTP drivers: Hide connection error message +- SimpleDB: Disable XML entity loader +- Latvian translation + +## Adminer 4.14.0 +- Use autofocus HTML attribute +- PostgreSQL: Fix initial value of exported autoincrement +- PostgreSQL: Fix renaming a database + +## Adminer 4.12.0 +- Fix SQL query code direction if RTL language is used +- MariaDB: Add support for UUID data type +- MS SQL, MongoDB: Connect to localhost with default port if server is not specified +- MongoDB: Fix parsing WHERE condition from SQL query + +## Adminer 4.11.0 +- MySQL: Fix highlighting current table in menu on macOS +- MariaDB: Fix several links to documentation pages +- MS SQL: Prefix Unicode strings with 'N' so they are treated correctly + +## Adminer 4.10.0 +- Print username next to the logout button +- Do not display empty action links in main menu + +## Adminer 4.9.4 +- Unify displaying of 'New item' action based on privileges +- Firefox: Fix opening a database to the new browser's tab with Ctrl+click +- Editor: Fix array conversion to string (issue adminerneo#3). +- Editor: Fix building links with array parameters + +## Adminer 4.9.3 +- MySQL, PostgreSQL: Fix queries splitting and string constants +- MySQL: Fix where clause for JSON column +- MySQL: Do not include unchanged PARTITION BY definition into ALTER TABLE query +- MariaDB: Support current_timestamp() +- PostgreSQL: Fix editing record that contains a field with GENERATED ALWAYS default value + +## Adminer 4.9.2 +- PostgreSQL: Fix search fields configuration (regression from 4.9.0) +- PostgreSQL: Fix exporting CREATE TABLE query with GENERATED default values +- PostgreSQL: Fix exporting CREATE TABLE with sequence default value +- PostgreSQL: Fix search condition for network address types, add macaddr8 type + +## Adminer 4.9.1 +- Support PHP 8.3 +- PostgreSQL: Show only accessible databases + +## Adminer 4.9.0 +- Validate connection to server in HTTP based drivers +- Elasticsearch 5: Make unusable driver usable again, move it to plugins +- Add new Elasticsearch 7 driver +- MySQL: Skip dump of generated columns + +## Adminer 4.8.2 +- Support multi-line table comments +- MySQL: Use ST_SRID() instead of SRID() for MySQL 8 (PR #418) +- PostgreSQL: Don't reset table comments (regression from 4.2.0) +- PostgreSQL PDO: Allow editing rows identified by boolean column (PR #380) + +## Adminer 4.8.1 (released 2021-05-14) +- Internet Explorer or PDO in Adminer 4.7.8-4.8.0: Fix XSS in doc_link (bug SF-797) +- Fix more PHP 8 warnings (bug SF-781) +- Avoid PHP warnings with PDO drivers (bug SF-786, regression from 4.7.8) +- MySQL: Allow moving views to other DB and renaming DB with views (bug SF-783) +- MariaDB: Do not treat sequences as views (PR #416) +- PostgreSQL: Support UPDATE OF triggers (bug SF-789) +- PostgreSQL: Support triggers with more events (OR) +- PostgreSQL: Fix parsing of foreign keys with non-ASCII column names +- PostgreSQL < 10 PDO: Avoid displaying GENERATED ALWAYS BY IDENTITY everywhere (bug SF-785, regression from 4.7.9) +- SQLite: Fix displayed types (bug SF-784, regression from 4.8.0) + +## Adminer 4.8.0 (released 2021-02-10) +- Support function default values in insert (bug SF-713) +- Allow SQL pseudo-function in insert +- Skip date columns for non-date values in search anywhere +- Add DB version to comment in export +- Support PHP 8 in create table (regression from 4.7.9) +- MySQL 8: Fix EXPLAIN in SQL command +- PostgreSQL: Create PRIMARY KEY for auto increment columns +- PostgreSQL: Avoid exporting empty sequence last value (bug SF-768) +- PostgreSQL: Do not show triggers from other schemas (PR #412) +- PostgreSQL: Fix multi-parameter functions in default values (bug SF-736) +- PostgreSQL: Fix displaying NULL bytea fields +- PostgreSQL PDO: Do not select NULL function for false values in edit +- Oracle: Alter indexes +- Oracle: Count tables +- Oracle: Import from CSV +- Oracle: Fix column size with string type +- MongoDB: Handle errors +- SimpleDB, Firebird, ClickHouse: Move to plugin + +## Adminer 4.7.9 (released 2021-02-07) +- Fix XSS in browsers which don't encode URL parameters (bug SF-775, regression from 4.7.0) +- Elasticsearch, ClickHouse: Do not print response if HTTP code is not 200 +- Don't syntax highlight during IME composition (bug SF-747) +- Quote values with leading and trailing zeroes in CSV export (bug SF-777) +- Link URLs in SQL command (PR #411) +- Fix displayed foreign key columns from other DB (bug SF-766) +- Re-enable PHP warnings (regression from 4.7.8) +- MySQL: Do not export names in quotes with sql_mode='ANSI_QUOTES' (bug SF-749) +- MySQL: Avoid error in PHP 8 when connecting to socket (PR #409) +- MySQL: Don't quote default value of text fields (bug SF-779) +- PostgreSQL: Export all FKs after all CREATE TABLE (PR #351) +- PostgreSQL: Fix dollar-quoted syntax highlighting (bug SF-738) +- PostgreSQL: Do not show view definition from other schema (PR #392) +- PostgreSQL: Use bigserial for bigint auto increment (bug SF-765, regression from 3.0.0) +- PostgreSQL PDO: Support PgBouncer, unsupport PostgreSQL < 9.1 (bug SF-771) +- PostgreSQL 10: Support GENERATED ALWAYS BY IDENTITY (PR #386) +- PostgreSQL 10: Support partitioned tables (PR #396) +- PostgreSQL 11: Create PRIMARY KEY for auto increment columns +- SQLite: Set busy_timeout to 500 +- MS SQL: Don't truncate comments to 30 chars (PR #376) +- Elasticsearch 6: Fix displaying type mapping (PR #402) +- MongoDB: Fix password-less check in the mongo extension (PR #405) +- Editor: Cast to string when searching (bug SF-325) +- Editor: Avoid trailing dot in export filename + +## Adminer 4.7.8 (released 2020-12-06) +- Support PHP 8 +- Disallow connecting to privileged ports (bug SF-769) + +## Adminer 4.7.7 (released 2020-05-11) +- Fix open redirect if Adminer is accessible at //adminer.php%2F@ + +## Adminer 4.7.6 (released 2020-01-31) +- Speed up alter table form (regression from 4.4.0) +- Fix clicking on non-input fields in alter table (regression from 4.6.2) +- Display time of procedure execution +- Disallow connecting to ports > 65535 (bug SF-730) +- MySQL: Always set foreign_key_checks in export +- PostgreSQL: Support exporting views +- Editor: Fix focusing foreign key search in select + +## Adminer 4.7.5 (released 2019-11-13) +- Add id="" to cells with failed inline edit (bug SF-708) +- PostgreSQL: Fix getting default value in PostgreSQL 12 (bug SF-719) +- PostgreSQL, Oracle: Set schema for EXPLAIN queries in SQL command (bug SF-706) +- ClickHouse: SQL command +- Swedish translation + +## Adminer 4.7.4 (released 2019-10-22) +- Fix XSS if Adminer is accessible at URL /data: + +## Adminer 4.7.3 (released 2019-08-27) +- Allow editing foreign keys pointing to tables in other database/schema (bug SF-694) +- Fix blocking of concurrent instances in PHP >7.2 (bug SF-703) +- MySQL: Speed up displaying tables in large databases (bug SF-700, regression from 4.7.2) +- MySQL: Allow editing rows identified by negative floats (bug SF-695) +- MySQL: Skip editing generated columns +- SQLite: Quote strings stored in integer columns in export (bug SF-696) +- SQLite: Handle error in altering table (bug SF-697) +- SQLite: Allow setting auto increment for empty tables +- SQLite: Preserve auto increment when recreating table +- MS SQL: Support foreign keys to other DB +- MongoDB: Allow setting authSource from environment variable + +## Adminer 4.7.2 (released 2019-07-18) +- Do not attempt logging in without password (bug SF-676) +- Stretch footer over the whole table width (bug SF-624) +- Allow overwriting tables when copying them +- Fix displaying SQL command after Save and continue edit +- Cache busting for adminer.css +- MySQL: Fix displaying multi-columns foreign keys (bug SF-675, regression from 4.7.0) +- MySQL: Fix creating users and changing password in MySQL 8 (bug SF-663) +- MySQL: Pass SRID to GeomFromText +- PostgreSQL: Fix setting column comments on new table +- PostgreSQL: Display definitions of materialized views (bug SF-682) +- PostgreSQL: Fix table status in PostgreSQL 12 (bug SF-683) +- MS SQL: Support comments +- Elasticsearch: Fix setting number of rows + +## Adminer 4.7.1 (released 2019-01-24) +- Display the tables scrollbar (bug SF-647) +- Remember visible columns in Create Table form (bug SF-493) +- Add autocomplete attributes to login form +- PHP <5.4 compatibility even with ClickHouse enabled (regression from 4.7.0) +- SQLite: Hide server field in login form +- Editor: Allow disabling boolean fields in PostgreSQL (bug SF-640) + +## Adminer 4.7.0 (released 2018-11-24) +- Simplify storing executed SQL queries to bookmarks +- Warn when using password with leading or trailing spaces +- Hide import from server if importServerPath() returns an empty string +- Fix inline editing of empty cells (regression from 4.6.3) +- Allow adding more than two indexes and forign key columns at a time (regression from 4.4.0) +- Avoid overwriting existing tables when copying tables (bug SF-642) +- Fix function change with set data type +- Increase username maxlength to 80 (bug SF-623) +- Make maxlength in all fields a soft limit +- Make tables horizontally scrollable +- MySQL: Support foreign keys created with ANSI quotes (bug SF-620) +- MySQL: Recognize ON UPDATE current_timestamp() (bug SF-632, bug SF-638) +- MySQL: Descending indexes in MySQL 8 (bug SF-643) +- PostgreSQL: Quote array values in export (bug SF-621) +- PostgreSQL: Export DESC indexes (bug SF-639) +- PostgreSQL: Support GENERATED BY DEFAULT AS IDENTITY in PostgreSQL 10 +- MS SQL: Pass database when connecting +- ClickHouse: Connect, databases list, tables list, select, SQL command +- Georgian translation + +## Adminer 4.6.3 (released 2018-06-28) +- Disallow using password-less databases +- Copy triggers when copying table +- Stop session before connecting +- Simplify running slow queries +- Decrease timeout for running slow queries from 5 seconds to 2 seconds +- Fix displaying info about non-alphabetical objects (bug SF-599) +- Use secure cookies on HTTP if session.cookie_secure is set +- PDO: Support binary fields download +- MySQL: Disallow LOAD DATA LOCAL INFILE +- MySQL: Use CONVERT() only when searching for non-ASCII (bug SF-603) +- MySQL: Order database names in MySQL 8 (bug SF-613) +- PostgreSQL: Fix editing data in views (bug SF-605, regression from 4.6.0) +- PostgreSQL: Do not cast date/time/number/uuid searches to text (bug SF-608) +- PostgreSQL: Export false as 0 in PDO (bug SF-619) +- MS SQL: Support port with sqlsrv +- Editor: Do not check boolean checkboxes with false in PostgreSQL (bug SF-607) + +## Adminer 4.6.2 (released 2018-02-20) +- Semi-transparent border on table actions +- Shorten JSON values in select (bug SF-594) +- Speed up alter table form (regression from 4.4.0) +- Store current version without authentication and in Editor +- PostgreSQL: Fix exporting string default values +- PostgreSQL: Fix exporting sequences in PostgreSQL 10 +- PostgreSQL: Add IF EXISTS to DROP SEQUENCE in export (bug SF-595) +- Editor: Fix displaying of true boolean values (regression from 4.5.0) + +## Adminer 4.6.1 (released 2018-02-09) +- Sticky position of table actions +- Speed up rendering of long tables (regression from 4.4.0) +- Display notification about performing action after relogin +- Add system tables help links +- MySQL: Support non-utf8 charset in search in column +- MySQL: Support geometry in MySQL 8 (bug SF-574) +- MariaDB: Links to documentation +- SQLite: Allow deleting PRIMARY KEY from tables with auto increment +- PostgreSQL: Support binary files in bytea fields +- PostgreSQL: Don't treat interval type as number (bug SF-474) +- PostgreSQL: Cast to string when searching using LIKE (bug SF-325) +- PostgreSQL: Fix condition for selecting no rows +- PostgreSQL: Support TRUNCATE+INSERT export +- Customization: Support connecting to MySQL via SSL +- Customization: Allow specifying server name displayed in breadcrumbs + +## Adminer 4.6.0 (released 2018-02-05) +- Fix counting selected rows after going back to select page +- PHP <5.3 compatibility even with Elasticsearch enabled +- Fully support functions in default values +- Stop redirecting links via adminer.org +- Support X-Forwarded-Prefix +- Display options for timestamp columns when creating a new table +- Disable autocompleting password on create user page +- Use primary key to edit rows even if not selected +- MySQL, PostgreSQL: Display warnings +- MySQL: Add floor and ceil select functions +- MySQL: Add FIND_IN_SET search operator +- MariaDB: Support JSON since MariaDB 10.2 +- SQLite, PostgreSQL: Limit rows in data manipulation without unique key +- PostgreSQL: Support routines +- PostgreSQL: Allow editing views with uppercase letters (bug SF-467) +- PostgreSQL: Allow now() as default value (bug SF-525) +- SimpleDB: Document that allow_url_fopen is required +- Malay translation + +## Adminer 4.5.0 (released 2018-01-24) +- Display name of the object in confirmation when dropping it +- Display newlines in column comments (bug SF-573) +- Support current_timestamp() as default of time fields (bug SF-572) +- Hide window.opener from pages opened in a new window (bug SF-561) +- Display error when getting row to edit +- Store current Adminer version server-side to avoid excessive requests +- Adminer: Fix Search data in tables (regression from 4.4.0) +- CSP: Allow any styles, images, media and fonts, disallow base-uri +- MySQL: Support geometry in MySQL 8 (bug SF-574) +- MySQL: Support routines with comments in parameters (bug SF-460) +- MariaDB: Support fulltext and spatial indexes in InnoDB (bug SF-583) +- SQLite: Enable foreign key checks +- PostgreSQL: Respect NULL default value +- PostgreSQL: Display foreign tables (bug SF-576) +- PostgreSQL: Do not export triggers if not requested +- PostgreSQL: Export DROP SEQUENCE if dropping table +- PostgreSQL: Display boolean values as code (bug SF-562) +- MS SQL: Support freetds +- non-MySQL: Avoid CONVERT() (bug SF-509) +- Elasticsearch: Insert, update, delete +- MongoDB: Support mongodb PHP extension +- Editor: Fix displaying of false values in PostgreSQL (bug SF-568) + +## Adminer 4.4.0 (released 2018-01-17) +- Add Content Security Policy +- Disallow scripts without nonce +- Rate limit password-less login attempts from the same IP address +- Disallow connecting to privileged ports +- Add nosniff header +- PHP 7.1: Prevent warning when using empty limit +- PHP 7.2: Prevent warning when searching in select +- MySQL: Remove dedicated view for replication status (added in 4.3.0) +- PostgreSQL: Sort table names (regression from 4.3.1) +- Editor: Don't set time zone from PHP, fixes DST +- Editor: Display field comment's text inside [] only in edit form +- Editor: Fix doubleclick on database page +- Editor: Fix Search data in tables +- Customization: Always send security headers +- Hebrew translation + +## Adminer 4.3.1 (released 2017-04-14) +- Fix permanent login after logout (bug SF-539) +- Fix SQL command autofocus (regression from 4.0.0) +- PostgreSQL: Support JSON and JSONB data types +- PostgreSQL: Fix index size computation in PostgreSQL < 9.0 (regression from 4.3.0) +- PostgreSQL: Fix nullable fields in export + +## Adminer 4.3.0 (released 2017-03-15) +- Make maxlength in edit fields a soft limit +- Add accessibility labels +- Add Cache-Control: immutable to static files +- MySQL: Support MySQL 8 +- MySQL: Support JSON data type +- MySQL: Add dedicated view for replication status +- MySQL: Support spatial indexes +- PostgreSQL: Export +- PostgreSQL: Don't treat partial indexes as unique +- MS SQL: Support pdo_dblib +- Elasticsearch: Support HTTPS by inputting https://server + +## Adminer 4.2.5 (released 2016-06-01) +- Fix remote execution in SQLite query +- SQLite: Require credentials to use +- PostgreSQL: Support KILL + +## Adminer 4.2.4 (released 2016-02-06) +- Fix remote execution in SQLite query +- MySQL: Support PHP 7 +- Bosnian translation +- Finnish translation + +## Adminer 4.2.3 (released 2015-11-15) +- Fix XSS in indexes (non-MySQL only) +- Support PHP 7 +- Greek translation +- Galician translation +- Bulgarian translation + +## Adminer 4.2.2 (released 2015-08-05) +- Fix XSS in alter table (found by HP Fortify) + +## Adminer 4.2.1 (released 2015-03-10) +- Send referrer header to the same domain +- MySQL: Fix usage of utf8mb4 if the client library doesn't support it +- MySQL: Use utf8mb4 in export only if required +- SQLite: Use EXPLAIN QUERY PLAN in SQL query + +## Adminer 4.2.0 (released 2015-02-07) +- Fix XSS in login form (bug SF-436) +- Allow limiting number of displayed rows in SQL command +- Fix reading routine column collations +- Unlock session in alter database +- Make master key unreadable to others (bug SF-410) +- Fix edit by long non-utf8 string +- Specify encoding for PHP 5.6 with invalid default_charset +- Fix saving NULL value, bug since Adminer 4.0.3 +- Send 403 for auth error +- Report offline and other AJAX errors (bug SF-419) +- Don't alter table comment if not changed +- Add links to documentation on table status page +- Fix handling of 64 bit numbers in auto_increment +- Add referrer: never meta tag +- MySQL: Use utf8mb4 if available +- MySQL: Support foreign keys in NDB storage +- PostgreSQL: Materialized views +- SQLite: Support CURRENT_* default values (bug SF-417) +- Elasticsearch: Use where in select +- Firebird: Alpha version +- Danish translation + +## Adminer 4.1.0 (released 2014-04-18) +- Provide size of all databases in the overview +- Prevent against brute force login attempts from the same IP address +- Compute number of tables in the overview explicitly +- Display edit form after error in clone or multi-edit +- Trim trailing non-breaking spaces in SQL textarea +- Display time of the select command +- Print elapsed time in HTML instead of SQL command comment +- Improve gzip export ratio (bug SF-387) +- Use rel="noreferrer" for external links, skip adminer.org redirect in WebKit +- MySQL: Fix enum types in routines (bug SF-391) +- MySQL: Fix editing rows by binary values, bug since Adminer 3.7.1 +- MySQL: Respect daylight saving time in dump, bug since Adminer 3.6.4 +- MySQL 5.6.5+: Support ON UPDATE on datatime column +- SQLite: Support UPDATE OF triggers +- SQLite: Display auto-created unique indexes, bug since Adminer 3.5.0 +- Editor: Fix login() method, bug since Adminer 4.0.0 +- Translate numbers in ar, bn, fa +- Vietnamese translation + +## Adminer 4.0.3 (released 2014-02-01) +- MongoDB: insert, truncate, indexes +- SimpleDB, MongoDB: insert more fields at once +- SQLite: Fix creating table and altering primary key, bug since Adminer 4.0.0 +- Don't store invalid credentials to session, bug since Adminer 4.0.0 +- Norweigan translation + +## Adminer 4.0.2 (released 2014-01-11) +- Fix handling of long text in SQL textarea +- Support paste to SQL textarea in Opera + +## Adminer 4.0.1 (released 2014-01-11) +- Don't use type=number if a SQL function is used +- Disable highlighting in textareas with long texts +- Don't autofocus SQL textarea in Firefox +- Don't link NULL foreign key values +- Fix displaying images in Editor, bug since Adminer 3.6.0 +- Fix uploading files, bug since Adminer 4.0.0 +- MongoDB: Count tables, display ObjectIds, sort, limit, offset, count rows +- Elasticsearch: Fix compiled version, create and drop DB, drop table + +## Adminer 4.0.0 (released 2014-01-08) +- Driver for SimpleDB, MongoDB and Elasticsearch +- Highlight SQL in textareas +- Save and continue edit by AJAX +- Split SQL command and import +- Add a new column in alter table on key press +- Mark length as required for strings +- Add label to database selection, move logout button +- Add button for dropping an index +- Display number of selected rows +- Add links to documentation +- Disable underlining links +- Differentiate views in navigation +- Improve speed of CSV import +- Keep form values after refresh in Firefox +- Mark auto_increment fields in edit +- Don't append newlines to uploaded files, bug since Adminer 3.7.0 +- Don't display SQL edit form on Ctrl+click on the select query, introduced in Adminer 3.6.4 +- Use MD5 for editing long keys only in supported drivers, bug since Adminer 3.6.4 +- Don't reset column when searching for an empty value with Enter, bug since Adminer 3.6.4 +- Encrypt passwords stored in session by a key stored in cookie +- Don't execute external JavaScript when verifying version +- Include JUSH in the compiled version +- Protect CSRF token against BREACH +- Non-MySQL: View triggers +- SQLite: Allow editing primary key +- SQLite: Allow editing foreign keys +- PostgreSQL: Fix handling of nextval() default values +- PostgreSQL: Support creating array columns +- Customization: Provide schemas() +- Portugal Portuguese translation +- Thai translation + +## Adminer 3.7.1 (released 2013-06-29) +- Increase click target for checkboxes +- Use shadow for highlighting default button +- Don't use LIMIT 1 if inline updating unique row +- Don't check previous checkbox on added column in create table (bug SF-326) +- Order table list by name +- Verify UTF-8 encoding of CSV import +- Notify user about expired master password for permanent login +- Highlight table being altered in navigation +- Send 404 for invalid database and schema +- Fix title and links on invalid table pages +- Display error on invalid alter table and view pages +- MySQL: Speed up updating rows without numeric or UTF-8 primary key +- Non-MySQL: Descending indexes +- PostgreSQL: Fix detecting oid column in PDO +- PostgreSQL: Handle timestamp types (bug SF-324) +- Korean translation + +## Adminer 3.7.0 (released 2013-05-19) +- Allow more SQL files to be uploaded at the same time +- Print run time next to executed queries +- Don't drop original view and routine before creating the new one +- Highlight default submit button +- Add server placeholder to login form +- Disable SQL export when applying functions in select +- Allow using lang() in plugins (customization) +- Remove bzip2 compression support +- Constraint memory used in TAR export +- Allow exporting views dependent on each other (bug SF-214) +- Fix resetting search (bug SF-318) +- Don't use LIMIT 1 if updating unique row (bug SF-320) +- Restrict editing rows without unique identifier to search results +- Display navigation below main content on mobile browsers +- Get number of rows on export page asynchronously +- Respect 'whole result' even if some rows are checked (bug SF-339 since Adminer 3.7.0) +- MySQL: Optimize create table page and Editor navigation +- MySQL: Display bit type as binary number +- MySQL: Improve export of binary data types +- MySQL: Fix handling of POINT data type (bug SF-282) +- MySQL: Don't export binary and geometry columns twice in select +- MySQL: Fix EXPLAIN in MySQL < 5.1, bug since Adminer 3.6.4 +- SQLite: Export views +- PostgreSQL: Fix swapped NULL and NOT NULL columns in PDO + +## Adminer 3.6.4 (released 2013-04-26) +- Display pagination on a fixed position +- Increase default select limit to 50 +- Display SQL edit form on Ctrl+click on the select query +- Display SQL history from newest +- Recover original view, trigger, routine if creating fails +- Do not store plain text password to history in creating user +- Selectable ON UPDATE CURRENT_TIMESTAMP field in create table +- Open database to a new window after selecting it with Ctrl +- Clear column name after resetting search (bug SF-296) +- Explain partitions in SQL query (bug SF-294) +- Allow loading more data with inline edit (bug SF-299) +- Stay on the same page after deleting rows (bug SF-301) +- Respect checked tables in export filename (bug SF-133) +- Respect PHP configuration max_input_vars +- Fix unsetting permanent login after logout +- Disable autocapitalize in identifiers on mobile browsers +- MySQL: Compatibility with MySQL 5.6 +- MySQL: Move ALTER export to plugin +- MySQL: Use numeric time zone in export +- MySQL: Link processlist documentation +- SQLite: Export indexes + +## Adminer 3.6.3 (released 2013-01-23) +- Display error code in SQL query +- Allow specifying external links +- Treat Meta key same as Ctrl +- Fix XSS in displaying non-UTF-8 strings +- Don't use type="number" for decimal numbers + +## Adminer 3.6.2 (released 2012-12-21) +- Edit values by Ctrl+click instead of double click +- Don't select row on double click +- Support NULL in routine calls +- Shorten printed values in varchar fields +- Display table default values on wide screens +- Display date in SQL history +- HTML5 input fields +- Display warning for missing UPDATE privilege +- Fix switching language on first load +- Support enabled mbstring.func_overload +- MySQL: Prolong comment length since MySQL 5.5 +- PostgreSQL: Fix process list in version 9.2 +- MS SQL: Support databases starting with number + +## Adminer 3.6.1 (released 2012-09-17) +- Fix compiled version on PHP with multibyte support + +## Adminer 3.6.0 (released 2012-09-16) +- Load more data in select +- Edit strings with \n in textarea +- Time out long running database list and select count +- Use VALUES() in INSERT+UPDATE export +- Style logout button as link +- Store selected database to permanent login +- Ctrl+click and Shift+click on button opens form to a blank window +- Switch language by POST +- Compress translations +- MySQL: Support geometry data types +- selectQueryBuild() method (customization) +- Serbian translation + +## Adminer 3.5.1 (released 2012-08-10) +- Support same name fields in CSV export +- Support Shift+click in export + +## Adminer 3.5.0 (released 2012-08-05) +- Links for column search in select +- Autohide column context menu in select +- Autodisplay long table names in tables list +- Display assigned auto_increment after clone +- SQLite: Full alter table +- SQLite: Better editing in tables without primary key +- SQLite: Display number of rows in database overview + +## Adminer 3.4.0 (released 2012-06-30) +- Link to descending order +- Shift+click on checkbox to select consecutive rows +- Print current time next to executed SQL queries +- Warn about selecting data without index +- Allow specifying database in login form +- Link to original table in EXPLAIN of SELECT * FROM table t +- Format numbers in translations +- MySQL: inform about disabled event_scheduler +- SQLite: support binary data +- PostgreSQL: approximate row count in table overview +- PostgreSQL: improve PDO support in SQL command +- Oracle: schema, processlist, table overview numbers +- Simplify work with NULL values (customization) +- Use namespace in login form (customization) +- Customizable export filename (customization) +- Replace JSMin by better JavaScript minifier +- Don't use AJAX links and forms +- Indonesian translation +- Ukrainian translation +- Bengali translation + +## Adminer 3.3.4 (released 2012-03-07) +- Foreign keys default actions (bug SF-188) +- SET DEFAULT foreign key action +- Fix minor parser bug in SQL command with webserver file +- Ctrl+click on button opens form to a blank window +- Trim table and column names (bug SF-195) +- Error message with no response from server in AJAX +- Esc to cancel AJAX request +- Move AJAX loading indicator to the right +- Don't quote bit type in export +- Don't check row while selecting text +- Fix invalid references line position on Database schema +- Disable selecting text on Database schema +- Ability to disable export (customization) +- Extensible list of databases (customization) +- MySQL: set autocommit after connect +- SQLite, PostgreSQL: vacuum +- SQLite, PostgreSQL: don't use LIKE for numbers (bug SF-202) +- PostgreSQL: fix alter foreign key +- PostgreSQL over PDO: connect if the eponymous database does not exist (bug SF-185) +- Boolean search (Editor) +- Persian translation + +## Adminer 3.3.3 (released 2011-08-12) +- Highlight checked rows +- Titles of links in database overview and navigation +- Fix trigger export (SQLite) +- Default trigger statement (SQLite, PostgreSQL) +- Remove search by expression (PostgreSQL, MS SQL) + +## Adminer 3.3.2 (released 2011-08-08) +- Display error with non-existent row in edit +- Fix minor parser bug in SQL command with webserver file +- Fix SQL command Stop on error +- Don't scroll with AJAX select order and alter move column +- Fast number of rows with big tables (PostgreSQL) +- Sort databases and schemas (PostgreSQL) + +## Adminer 3.3.1 (released 2011-07-27) +- Fix XSS introduced in Adminer 3.2.0 +- Fix altering default values (PostgreSQL) +- Process list (PostgreSQL) + +## Adminer 3.3.0 (released 2011-07-19) +- Use Esc to disable in-place edit +- Shortcut for database privileges +- Editable index names +- Append new index with auto index selection (bug SF-138) +- Preserve original timestamp value in multiple update (bug SF-158) +- Bit type default value +- Display foreign key name in tooltip +- Display default column value in table overview +- Display column collation in tooltip +- Keyboard shortcuts: Alt+Shift+1 for homepage, Ctrl+Shift+Enter for Save and continue edit +- Show only errors with Webserver file SQL command +- Remember select export and import options +- Link tables and indexes from SQL command EXPLAIN (MySQL) +- Display error with all wrong SQL commands (MySQL) +- Display foreign keys from other schemas (PostgreSQL) +- Pagination support (Oracle) +- Autocomplete for big foreign keys (Editor) +- Display name of the referenced record in PostgreSQL (Editor) +- Prefer NULL to empty string (Editor, bug SF-162) +- Display searched columns (Editor) +- Customizable favicon (customization) +- Method name can return a link (customization) +- Easier sending of default headers (customization) +- Lithuanian and Romanian translation + +## Adminer 3.2.2 (released 2011-03-28) +- Fix AJAX history after reload + +## Adminer 3.2.1 (released 2011-03-23) +- Ability to save expression in edit +- Respect default database collation (bug SF-119) +- Don't export triggers without table (bug SF-123) +- Esc to focus next field in Tab textarea +- Send forms by Ctrl+Enter on <select> +- Enum editor and textarea Ctrl+Enter working in IE +- AJAX forms in Google Chrome +- Parse UTF-16 and UTF-8 BOM in all text uploads +- Display ; in history +- Use DELIMITER in history +- Show databases even with skip_show_database in MySQL 5 +- Disable maxlength with functions in edit +- Better placement of AJAX icon +- Table header in CSV export (Editor) +- Time format hint (Editor) +- Respect order after search (Editor) +- Set MySQL time zone by PHP setting (Editor) +- Allow own code in <head> (customization) +- Polish translation + +## Adminer 3.2.0 (released 2011-02-24) +- Get long texts and slow information by AJAX +- Most links and forms by AJAX in browsers with support for history.pushState +- Copy tables +- Ability to search by expression in select +- Export SQL command result (bug SF-99) +- Focus first field with insert (bug SF-106) +- Permanent link in schema +- Display total time in show only errors mode in SQL command +- History: edit all +- MS SQL: auto primary and foreign key +- SQLite: display 0 +- Create table default data type: int +- Focus upper/lower fields by Ctrl+Up/Ctrl+Down +- Hide credentials for SQLite +- Utilize oids in PostgreSQL +- Homepage customization +- Use IN for search in numeric fields (Editor) +- Use password input for _md5 and _sha1 fields (Editor) +- Work without session.use_cookies (bug SF-107) +- Fix saving schema to cookie in Opera +- Portuguese, Slovenian and Turkish translation + +## Adminer 3.1.0 (released 2010-11-16) +- TSV export and import +- Customizable export +- Option to show only errors in SQL command +- Link to bookmark SQL command +- Recognize $$ strings in SQL command (PostgreSQL) +- Highlight and edit SQL command in processlist +- Always display all drivers +- Timestamp at the end of export +- Link to refresh database cache (bug SF-96) +- Support for virtual foreign keys +- Disable XSS "protection" of IE8 +- Immunity against zend.ze1_compatibility_mode (bug SF-86) +- Fix last page with empty result set +- Arabic translation and RTL support +- Dual licensing: Apache or GPL + +## Adminer 3.0.1 (released 2010-10-18) +- Send the form by Ctrl+Enter in all textareas +- Disable creating SQLite databases with extension other than db, sdb, sqlite +- Ability to use Adminer in a frame through customization +- Catalan translation +- MS SQL 2005 compatibility +- PostgreSQL: connect if the eponymous database does not exist + +## Adminer 3.0.0 (released 2010-10-15) +- Drivers for MS SQL, SQLite, PostgreSQL, Oracle +- Allow concurrent logins on the same server +- Allow permanent login without customization +- In-place editation in select +- Foreign key options in Table creation +- Treat binary type as hex +- Show number of tables in server overview +- Operator LIKE %% +- Remember export parameters in cookie +- Allow semicolon as CSV separator +- Schemas, sequences and types support (PostgreSQL) +- Autofocus username in login form +- Allow to insert Tab in SQL textareas and send the form by Ctrl+Enter +- Disable spellchecking in SQL textareas +- Display auto_increment value of inserted item +- Allow disabling auto_increment value export +- Prefill auto_increment column name +- Ability to jump to any page in select by JavaScript +- Display comment in table overview +- Link last page above data in select +- Link table names in SQL queries +- Hungarian, Japanese and Tamil translation +- Defer table information in database overview to JavaScript (performance) +- Big tables optimizations (performance) + +## Adminer 2.3.2 (released 2010-04-21) +- Fix COUNT(*) link +- Fix Save and continue edit + +## Adminer 2.3.1 (released 2010-04-06) +- Add Drop button to Alter pages (regression from 2.0.0) +- Link COUNT(*) result to listing +- Newlines in select query edit +- Return to referrer after edit +- Respect session.auto_start (bug SF-42) + +## Adminer 2.3.0 (released 2010-02-26) +- Support for permanent login (customization required) +- Search in all tables +- Show status variables +- Print sums in tables overview +- Add Delete button to Edit page (regression from 2.0.0) +- Print error summary in SQL command +- Simplify SQL syntax error message +- Show SQL query info if available +- Delete length when changing type in alter table +- Ability to check table prefix in export + +## Adminer 2.2.1 (released 2009-11-26) +- Highlight current links +- Improve concurrency +- Move number of tables to DB info (performance) +- Search by foreign keys (Editor) +- Link new item in backward keys (Editor) + +## Adminer 2.2.0 (released 2009-10-20) +- Database list - bulk drop, number of tables +- Enlarge field for enum and set definition +- Display table links above table structure +- Link URLs in select +- Display number of manipulated rows in JS confirm +- Set required memory in SQL command +- Fix removed default in ALTER +- Display whitespace in texts (bug SF-11) +- ClickJacking protection in modern browsers +- E-mail attachments (Editor) +- Optional year in date (Editor) +- Search operators (Editor) +- Align numbers to right in select (Editor) +- Move <h1> to $adminer->navigation (customization) +- Rename get_dbh to connection (customization) + +## Adminer 2.1.0 (released 2009-09-12) +- Edit default values directly in table creation +- Execute SQL file stored on server disk +- Display EXPLAIN in SQL query +- Compress export and import +- Display column comments in table overview +- Use ON DUPLICATE KEY UPDATE for CSV import +- Print ALTER export instead of executing it +- Click on row selects it +- Fix Editor date format +- Fix long SQL query crash (bug SF-3) +- Speed up simple alter table +- Traditional Chinese translation + +## Adminer 2.0.0 (released 2009-08-06) +- Editor: User friendly data editor +- Customization: Adminer class +- Create single column foreign key in table structure +- Table relations (Editor) +- Send e-mails (Editor) +- Display images in blob (Editor) +- Localize date (Editor) +- Treat tinyint(1) as bool (Editor) +- Divide types to groups in table creation +- Link e-mails in select +- Show type in field name title +- Preselect now() for timestamp columns +- Clear history +- Prefill insert by foreign key searches +- Print number of rows in SQL command +- Remove Delete button from Edit page - use mass operation for it +- Faster multiple update, clone and delete +- Faster table list in navigation +- Download version checker and syntax highlighting from HTTPS +- Use HTML Strict instead of XHTML +- Remove function minification in favor of performance and customization +- Fix grant ALL PRIVILEGES with GRANT OPTION +- Fix CSV import +- Fix work with default values + +## Adminer 1.11.1 (released 2009-07-03) +- Fix problem with enabled Filter extension + +## Adminer 1.11.0 (released 2009-07-02) +- Connection through socket by server :/path/to/socket +- Simplify export +- Display execution time in SQL query +- Relative date and time functions +- Version checker +- Save queries to history and display it on SQL page +- Display MySQL variables +- Ability to select all rows on current page of select +- Separate JavaScript functions +- Always use the default style before the external one +- Always try to use the syntax highlighter +- All privileges in user rights +- Fix FOUND_ROWS() in SQL command +- Export only selected columns in select +- Bulk database creation +- Include views in drop and move on database overview +- Hide fieldsets in select +- Automatically add new fields in table creation +- Use \n in SQL commands + +## phpMinAdmin 1.10.1 (released 2009-05-07) +- Highlight odd and hover rows +- Partition editing comfort (bug SF-12) +- Allow full length in limited int + +## phpMinAdmin 1.10.0 (released 2009-04-28) +- Partitioning (MySQL 5.1) +- CSV import +- Plus and minus functions +- Option to stop on error in SQL command +- Cross links to select and table (bug SF-5), link new item +- Suhosin compatibility +- Remove max_allowed_packet from export +- Read style from phpMinAdmin.css if exists +- Size reduction by minification of variables and functions +- Russian translation + +## phpMinAdmin 1.9.1 (released 2008-10-27) +- Update translations + +## phpMinAdmin 1.9.0 (released 2008-10-16) +- List of tables and views with maintenance commands +- Clone rows +- Bulk edit and clone +- Function results in edit +- NOT operators in select +- Search without column restriction +- Use type=password for unhashed password +- Only one button for each action in select +- Choose language through option-list +- XHTML syntax errors +- Don't set global variable in export +- SHOW DATABASES can be revoked +- Order by function result working also in older MySQL versions +- Tested on IIS + +## phpMinAdmin 1.8.0 (released 2008-09-12) +- Events (MySQL 5.1) +- Access without login - accept ?username= +- Print SQL query in select, messages and warnings +- Display number of found rows +- Don't wrap lines in select table +- Italian and Estonian translation +- Order by COUNT(*) + +## phpMinAdmin 1.7.0 (released 2008-08-26) +- Customizable export (select objects to export, SQL or CSV) +- Ability to alter existing tables and drop old tables in export +- Choose columns in select, aggregation +- Order rows by clicking on table heading +- Truncate only search results +- Automatically select name for trigger +- Chinese and French translation +- Preserve default values when altering table +- Maintain auto_increment when moving columns +- Smaller multilingual file +- Cache static files +- Faster checking of number of results + +## phpMinAdmin 1.6.1 (released 2008-05-22) +- Set session parameters only if not session.auto_start + +## phpMinAdmin 1.6.0 (released 2008-05-16) +- Order of columns in table +- Set max_allowed_packet in dump and use extended insert +- Spanish and German translations +- Use images for editing buttons +- Protection against big POST data +- Logout by POST +- Information about logged user +- Separate stylesheet +- Last-Modified header for files +- Several bug fixes + +## phpMinAdmin 1.5.0 (released 2008-01-09) +- Mass delete +- Vertical privileges +- Specify connection port by colon in server +- Ignore length in date and time types +- Boolean fulltext search for all columns in MyISAM +- Shrink compiled output +- Remove maxlength from server and username +- Uncheck NULL by change +- Mark shortened fields in select + +## phpMinAdmin 1.4.0 (released 2007-08-15) +- Privileges +- New design +- Dutch translation +- Use NULL for auto_increment (bug SF-1) +- Fix dropping procedure parameters + +## phpMinAdmin 1.3.2 (released 2007-08-06) +- Next field by JavaScript in foreign keys +- Set time zone in dump +- Refresh lang cookie +- Remember drop result in case of faulty create +- Move vertical lines in schema properly +- Fix maximum page in select + +## phpMinAdmin 1.3.1 (released 2007-07-31) +- Move references lines in schema +- Fix dump +- Fix update links + +## phpMinAdmin 1.3.0 (released 2007-07-27) +- Breadcrumb navigation +- Operator IN +- Timestamp default values +- Draggable tables in schema +- Number of rows in navigation +- Display MySQL version and used PHP extension +- More friendly user interface +- Slovak translation + +## phpMinAdmin 1.2.0 (released 2007-07-25) +- Manipulate triggers +- PDO Abstraction +- Auto_increment value +- JavaScript for adding rows + +## phpMinAdmin 1.1.0 (released 2007-07-19) +- Routines manipulation +- Views manipulation +- Foreign keys manipulation +- Database schema with references +- Processlist +- Index length +- Dump individual tables +- JavaScript for next rows in table edit +- Cache databases list + +## phpMinAdmin 1.0.0 (released 2007-07-11) +- First official release + +`SF-` means https://sourceforge.net/p/adminer/bugs-and-features/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..6889d8443 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +- Reproducible [bug reports](https://github.com/vrana/adminer/issues/new?template=bug_report.md) are warmly welcomed. +- [Feature requests](https://github.com/vrana/adminer/issues/new?template=BLANK_ISSUE) are also fine, but I'm quite picky about what to accept into Adminer. Please don't be offended if I close the issue as "Not Planned," especially if it can be achieved with a plugin. +- [Pull requests](https://github.com/vrana/adminer/pulls) for both bug fixes and simple features are welcome. Before working on anything more complicated, get familiar with the [Adminer philosophy](https://github.com/vrana/adminer/blob/master/developing.md). diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..f9308f92a --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +Apache License 2.0 or GPL 2 diff --git a/README.md b/README.md new file mode 100644 index 000000000..856aab018 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Adminer +**Adminer** is a full-featured database management tool written in PHP. It consists of a single file ready to deploy to the target server. +**Adminer Editor** offers data manipulation for end-users. + +[Official Website](https://www.adminer.org/) + +## Features +- **Supports:** MySQL, MariaDB, PostgreSQL, CockroachDB, SQLite, MS SQL, Oracle +- **Plugins for:** Elasticsearch, SimpleDB, MongoDB, Firebird, ClickHouse, IMAP +- **Requirements:** PHP 5.3+ (compiled file), PHP 7.4+ (source codes) + +## Screenshot +![Table structure](https://www.adminer.org/static/screenshots/table.png) + +## Installation +If downloaded from Git then run: `git submodule update --init` + +- `adminer/index.php` - Run development version of Adminer +- `editor/index.php` - Run development version of Adminer Editor +- `editor/example.php` - Example customization +- `compile.php` - Create a single file version +- `lang.php` - Update translations +- `tests/*.html` - Katalon Recorder test suites + +## Plugins +There are several plugins distributed with Adminer, as well as many user-contributed plugins listed on the [Adminer Plugins page](https://www.adminer.org/plugins/). diff --git a/SECURITY.md b/SECURITY.md index c68b159e1..219cda5b8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,10 +2,10 @@ ## Supported Versions -I support only the last published version and the last development version (last commit). +Only the latest published version and the latest development version (last commit) are supported. ## Reporting a Vulnerability -To report a vulnerability, create a private bug at https://sourceforge.net/p/adminer/bugs-and-features/new/?private=1. +To report a vulnerability, create a new draft security advisory at [GitHub Security Advisories](https://github.com/vrana/adminer/security/advisories/new). -I handle security issues with top priority. If you don't hear from me in a week then please ping the bug. Once I accept the bug, the fix should be available and new version released within days. I will mark the bug as public after releasing a new version or declining the bug. +Security issues are handled with top priority. If you don't receive a response within a week, please follow up on the report. Once a vulnerability is acknowledged, a fix should be available and a new version released within a few days. The issue will be made public after the fix is released or if the report is declined. diff --git a/adminer-5.4.2.php b/adminer-5.4.2.php new file mode 100644 index 000000000..4827efaef --- /dev/null +++ b/adminer-5.4.2.php @@ -0,0 +1,1771 @@ +credentials();$J=Driver::connect($Hb[0],$Hb[1],$Hb[2]);return(is_object($J)?$J:null);}function +idf_unescape($v){if(!preg_match('~^[`\'"[]~',$v))return$v;$Le=substr($v,-1);return +str_replace($Le.$Le,$Le,substr($v,1,-1));}function +q($Q){return +connection()->quote($Q);}function +escape_string($X){return +substr(q($X),1,-1);}function +idx($xa,$y,$l=null){return($xa&&array_key_exists($y,$xa)?$xa[$y]:$l);}function +number($X){return +preg_replace('~[^0-9]+~','',$X);}function +number_type(){return'((?$X)$J[stripslashes($y)]=(is_array($X)?remove_slashes($X,$bd):($bd?$X:stripslashes($X)));return$J;}function +bracket_escape($v,$Ea=false){static$gj=array(':'=>':1',']'=>':2','['=>':3','"'=>':4');return +strtr($v,($Ea?array_flip($gj):$gj));}function +min_version($Qj,$af="",$h=null){$h=connection($h);$Zh=$h->server_info;if($af&&preg_match('~([\d.]+)-MariaDB~',$Zh,$A)){$Zh=$A[1];$Qj=$af;}return$Qj&&version_compare($Zh,$Qj)>=0;}function +charset(Db$g){return(min_version("5.5.3",0,$g)?"utf8mb4":"utf8");}function +ini_bool($le){$X=ini_get($le);return(preg_match('~^(on|true|yes)$~i',$X)||(int)$X);}function +ini_bytes($le){$X=ini_get($le);switch(strtolower(substr($X,-1))){case'g':$X=(int)$X*1024;case'm':$X=(int)$X*1024;case'k':$X=(int)$X*1024;}return$X;}function +sid(){static$J;if($J===null)$J=(SID&&!($_COOKIE&&ini_bool("session.use_cookies")));return$J;}function +set_password($Pj,$N,$V,$F){$_SESSION["pwds"][$Pj][$N][$V]=($_COOKIE["adminer_key"]&&is_string($F)?array(encrypt_string($F,$_COOKIE["adminer_key"])):$F);}function +get_password(){$J=get_session("pwds");if(is_array($J))$J=($_COOKIE["adminer_key"]?decrypt_string($J[0],$_COOKIE["adminer_key"]):false);return$J;}function +get_val($H,$n=0,$vb=null){$vb=connection($vb);$I=$vb->query($H);if(!is_object($I))return +false;$K=$I->fetch_row();return($K?$K[$n]:false);}function +get_vals($H,$d=0){$J=array();$I=connection()->query($H);if(is_object($I)){while($K=$I->fetch_row())$J[]=$K[$d];}return$J;}function +get_key_vals($H,$h=null,$ci=true){$h=connection($h);$J=array();$I=$h->query($H);if(is_object($I)){while($K=$I->fetch_row()){if($ci)$J[$K[0]]=$K[1];else$J[]=$K[0];}}return$J;}function +get_rows($H,$h=null,$m="

"){$vb=connection($h);$J=array();$I=$vb->query($H);if(is_object($I)){while($K=$I->fetch_assoc())$J[]=$K;}elseif(!$I&&!$h&&$m&&(defined('Adminer\PAGE_HEADER')||$m=="-- "))echo$m.error()."\n";return$J;}function +unique_array($K,array$x){foreach($x +as$w){if(preg_match("~PRIMARY|UNIQUE~",$w["type"])&&!$w["partial"]){$J=array();foreach($w["columns"]as$y){if(!isset($K[$y]))continue +2;$J[$y]=$K[$y];}return$J;}}}function +escape_key($y){if(preg_match('(^([\w(]+)('.str_replace("_",".*",preg_quote(idf_escape("_"))).')([ \w)]+)$)',$y,$A))return$A[1].idf_escape(idf_unescape($A[2])).$A[3];return +idf_escape($y);}function +where(array$Z,array$o=array()){$J=array();foreach((array)$Z["where"]as$y=>$X){$y=bracket_escape($y,true);$d=escape_key($y);$n=idx($o,$y,array());$Yc=$n["type"];$J[]=$d.(JUSH=="sql"&&$Yc=="json"?" = CAST(".q($X)." AS JSON)":(JUSH=="pgsql"&&preg_match('~^json~',$Yc)?"::jsonb = ".q($X)."::jsonb":(JUSH=="sql"&&is_numeric($X)&&preg_match('~\.~',$X)?" LIKE ".q($X):(JUSH=="mssql"&&strpos($Yc,"datetime")===false?" LIKE ".q(preg_replace('~[_%[]~','[\0]',$X)):" = ".unconvert_field($n,q($X))))));if(JUSH=="sql"&&preg_match('~char|text~',$Yc)&&preg_match("~[^ -@]~",$X))$J[]="$d = ".q($X)." COLLATE ".charset(connection())."_bin";}foreach((array)$Z["null"]as$y)$J[]=escape_key($y)." IS NULL";return +implode(" AND ",$J);}function +where_check($X,array$o=array()){parse_str($X,$Ya);remove_slashes(array(&$Ya));return +where($Ya,$o);}function +where_link($t,$d,$Y,$cg="="){return"&where%5B$t%5D%5Bcol%5D=".urlencode($d)."&where%5B$t%5D%5Bop%5D=".urlencode(($Y!==null?$cg:"IS NULL"))."&where%5B$t%5D%5Bval%5D=".urlencode($Y);}function +convert_fields(array$e,array$o,array$M=array()){$J="";foreach($e +as$y=>$X){if($M&&!in_array(idf_escape($y),$M))continue;$ya=convert_field($o[$y]);if($ya)$J +.=", $ya AS ".idf_escape($y);}return$J;}function +cookie($B,$Y,$Te=2592000){header("Set-Cookie: $B=".rawurlencode($Y).($Te?"; expires=".gmdate("D, d M Y H:i:s",time()+$Te)." GMT":"")."; path=".preg_replace('~\?.*~','',$_SERVER["REQUEST_URI"]).(HTTPS?"; secure":"")."; HttpOnly; SameSite=lax",false);}function +get_settings($Db){parse_str($_COOKIE[$Db],$di);return$di;}function +get_setting($y,$Db="adminer_settings",$l=null){return +idx(get_settings($Db),$y,$l);}function +save_settings(array$di,$Db="adminer_settings"){$Y=http_build_query($di+get_settings($Db));cookie($Db,$Y);$_COOKIE[$Db]=$Y;}function +restart_session(){if(!ini_bool("session.use_cookies")&&(!function_exists('session_status')||session_status()==1))session_start();}function +stop_session($jd=false){$Gj=ini_bool("session.use_cookies");if(!$Gj||$jd){session_write_close();if($Gj&&@ini_set("session.use_cookies",'0')===false)session_start();}}function&get_session($y){return$_SESSION[$y][DRIVER][SERVER][$_GET["username"]];}function +set_session($y,$X){$_SESSION[$y][DRIVER][SERVER][$_GET["username"]]=$X;}function +auth_url($Pj,$N,$V,$k=null){$Cj=remove_from_uri(implode("|",array_keys(SqlDriver::$drivers))."|username|ext|".($k!==null?"db|":"").($Pj=='mssql'||$Pj=='pgsql'?"":"ns|").session_name());preg_match('~([^?]*)\??(.*)~',$Cj,$A);return"$A[1]?".(sid()?SID."&":"").($Pj!="server"||$N!=""?urlencode($Pj)."=".urlencode($N)."&":"").($_GET["ext"]?"ext=".urlencode($_GET["ext"])."&":"")."username=".urlencode($V).($k!=""?"&db=".urlencode($k):"").($A[2]?"&$A[2]":"");}function +is_ajax(){return($_SERVER["HTTP_X_REQUESTED_WITH"]=="XMLHttpRequest");}function +redirect($We,$qf=null){if($qf!==null){restart_session();$_SESSION["messages"][preg_replace('~^[^?]*~','',($We!==null?$We:$_SERVER["REQUEST_URI"]))][]=$qf;}if($We!==null){if($We=="")$We=".";header("Location: $We");exit;}}function +query_redirect($H,$We,$qf,$nh=true,$Kc=true,$Tc=false,$Ti=""){if($Kc){$si=microtime(true);$Tc=!connection()->query($H);$Ti=format_time($si);}$mi=($H?adminer()->messageQuery($H,$Ti,$Tc):"");if($Tc){adminer()->error +.=error().$mi.script("messagesPrint();")."
";return +false;}if($nh)redirect($We,$qf.$mi);return +true;}class +Queries{static$queries=array();static$start=0;}function +queries($H){if(!Queries::$start)Queries::$start=microtime(true);Queries::$queries[]=(driver()->delimiter!=';'?$H:(preg_match('~;$~',$H)?"DELIMITER ;;\n$H;\nDELIMITER ":$H).";");return +connection()->query($H);}function +apply_queries($H,array$T,$Gc='Adminer\table'){foreach($T +as$R){if(!queries("$H ".$Gc($R)))return +false;}return +true;}function +queries_redirect($We,$qf,$nh){$ih=implode("\n",Queries::$queries);$Ti=format_time(Queries::$start);return +query_redirect($ih,$We,$qf,$nh,false,!$nh,$Ti);}function +format_time($si){return +lang(0,max(0,microtime(true)-$si));}function +relative_uri(){return +str_replace(":","%3a",preg_replace('~^[^?]*/([^?]*)~','\1',$_SERVER["REQUEST_URI"]));}function +remove_from_uri($zg=""){return +substr(preg_replace("~(?<=[?&])($zg".(SID?"":"|".session_name()).")=[^&]*&~",'',relative_uri()."&"),0,-1);}function +get_file($y,$Tb=false,$Zb=""){$ad=$_FILES[$y];if(!$ad)return +null;foreach($ad +as$y=>$X)$ad[$y]=(array)$X;$J='';foreach($ad["error"]as$y=>$m){if($m)return$m;$B=$ad["name"][$y];$bj=$ad["tmp_name"][$y];$_b=file_get_contents($Tb&&preg_match('~\.gz$~',$B)?"compress.zlib://$bj":$bj);if($Tb){$si=substr($_b,0,3);if(function_exists("iconv")&&preg_match("~^\xFE\xFF|^\xFF\xFE~",$si))$_b=iconv("utf-16","utf-8",$_b);elseif($si=="\xEF\xBB\xBF")$_b=substr($_b,3);}$J +.=$_b;if($Zb)$J +.=(preg_match("($Zb\\s*\$)",$_b)?"":$Zb)."\n\n";}return$J;}function +upload_error($m){$kf=($m==UPLOAD_ERR_INI_SIZE?ini_get("upload_max_filesize"):0);return($m?lang(1).($kf?" ".lang(2,$kf):""):lang(3));}function +repeat_pattern($Lg,$Re){return +str_repeat("$Lg{0,65535}",$Re/65535)."$Lg{0,".($Re%65535)."}";}function +is_utf8($X){return(preg_match('~~u',$X)&&!preg_match('~[\0-\x8\xB\xC\xE-\x1F]~',$X));}function +format_number($X){return +strtr(number_format($X,0,".",lang(4)),preg_split('~~u',lang(5),-1,PREG_SPLIT_NO_EMPTY));}function +friendly_url($X){return +preg_replace('~\W~i','-',$X);}function +table_status1($R,$Uc=false){$J=table_status($R,$Uc);return($J?reset($J):array("Name"=>$R));}function +column_foreign_keys($R){$J=array();foreach(adminer()->foreignKeys($R)as$q){foreach($q["source"]as$X)$J[$X][]=$q;}return$J;}function +fields_from_edit(){$J=array();foreach((array)$_POST["field_keys"]as$y=>$X){if($X!=""){$X=bracket_escape($X);$_POST["function"][$X]=$_POST["field_funs"][$y];$_POST["fields"][$X]=$_POST["field_vals"][$y];}}foreach((array)$_POST["fields"]as$y=>$X){$B=bracket_escape($y,true);$J[$B]=array("field"=>$B,"privileges"=>array("insert"=>1,"update"=>1,"where"=>1,"order"=>1),"null"=>1,"auto_increment"=>($y==driver()->primary),);}return$J;}function +dump_headers($Rd,$Af=false){$J=adminer()->dumpHeaders($Rd,$Af);$vg=$_POST["output"];if($vg!="text")header("Content-Disposition: attachment; filename=".adminer()->dumpFilename($Rd).".$J".($vg!="file"&&preg_match('~^[0-9a-z]+$~',$vg)?".$vg":""));session_write_close();if(!ob_get_level())ob_start(null,4096);ob_flush();flush();return$J;}function +dump_csv(array$K){$pj=$_POST["format"]=="tsv";foreach($K +as$y=>$X){if(preg_match('~["\n]|^0[^.]|\.\d*0$|'.($pj?'\t':'[,;]|^$').'~',$X))$K[$y]='"'.str_replace('"','""',$X).'"';}echo +implode(($_POST["format"]=="csv"?",":($pj?"\t":";")),$K)."\r\n";}function +apply_sql_function($s,$d){return($s?($s=="unixepoch"?"DATETIME($d, '$s')":($s=="count distinct"?"COUNT(DISTINCT ":strtoupper("$s("))."$d)"):$d);}function +get_temp_dir(){$J=ini_get("upload_tmp_dir");if(!$J){if(function_exists('sys_get_temp_dir'))$J=sys_get_temp_dir();else{$p=@tempnam("","");if(!$p)return'';$J=dirname($p);unlink($p);}}return$J;}function +file_open_lock($p){if(is_link($p))return;$r=@fopen($p,"c+");if(!$r)return;@chmod($p,0660);if(!flock($r,LOCK_EX)){fclose($r);return;}return$r;}function +file_write_unlock($r,$Nb){rewind($r);fwrite($r,$Nb);ftruncate($r,strlen($Nb));file_unlock($r);}function +file_unlock($r){flock($r,LOCK_UN);fclose($r);}function +first(array$xa){return +reset($xa);}function +password_file($i){$p=get_temp_dir()."/adminer.key";if(!$i&&!file_exists($p))return'';$r=file_open_lock($p);if(!$r)return'';$J=stream_get_contents($r);if(!$J){$J=rand_string();file_write_unlock($r,$J);}else +file_unlock($r);return$J;}function +rand_string(){return +md5(uniqid(strval(mt_rand()),true));}function +select_value($X,$_,array$n,$Si){if(is_array($X)){$J="";if(array_filter($X,'is_array')==array_values($X)){$Ee=array();foreach($X +as$W)$Ee+=array_fill_keys(array_keys($W),null);foreach(array_keys($Ee)as$Ce)$J +.="".h($Ce);foreach($X +as$W){$J +.="";foreach(array_merge($Ee,$W)as$Kj)$J +.="".select_value($Kj,$_,$n,$Si);}}else{foreach($X +as$Ce=>$W)$J +.="".($X!=array_values($X)?"".h($Ce):"")."".select_value($W,$_,$n,$Si);}return"$J
";}if(!$_)$_=adminer()->selectLink($X,$n);if($_===null){if(is_mail($X))$_="mailto:$X";if(is_url($X))$_=$X;}$J=adminer()->editVal(driver()->value($X,$n),$n);if($J!==null){if(!is_utf8($J))$J="\0";elseif($Si!=""&&is_shortable($n))$J=shorten_utf8($J,max(0,+$Si));else$J=h($J);}return +adminer()->selectVal($J,$_,$n,$X);}function +is_blob(array$n){return +preg_match('~blob|bytea|raw|file~',$n["type"])&&!in_array($n["type"],idx(driver()->structuredTypes(),lang(6),array()));}function +is_mail($uc){$za='[-a-z0-9!#$%&\'*+/=?^_`{|}~]';$hc='[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])';$Lg="$za+(\\.$za+)*@($hc?\\.)+$hc";return +is_string($uc)&&preg_match("(^$Lg(,\\s*$Lg)*\$)i",$uc);}function +is_url($Q){$hc='[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])';return +preg_match("~^((https?):)?//($hc?\\.)+$hc(:\\d+)?(/.*)?(\\?.*)?(#.*)?\$~i",$Q);}function +is_shortable(array$n){return!preg_match('~'.number_type().'|date|time|year~',$n["type"]);}function +host_port($N){return(preg_match('~^(\[(.+)]|([^:]+)):([^:]+)$~',$N,$A)?array($A[2].$A[3],$A[4]):array($N,''));}function +count_rows($R,array$Z,$ve,array$xd){$H=" FROM ".table($R).($Z?" WHERE ".implode(" AND ",$Z):"");return($ve&&(JUSH=="sql"||count($xd)==1)?"SELECT COUNT(DISTINCT ".implode(", ",$xd).")$H":"SELECT COUNT(*)".($ve?" FROM (SELECT 1$H GROUP BY ".implode(", ",$xd).") x":$H));}function +slow_query($H){$k=adminer()->database();$Ui=adminer()->queryTimeout();$hi=driver()->slowQuery($H,$Ui);$h=null;if(!$hi&&support("kill")){$h=connect();if($h&&($k==""||$h->select_db($k))){$Fe=get_val(connection_id(),0,$h);echo +script("const timeout = setTimeout(() => { ajax('".js_escape(ME)."script=kill', function () {}, 'kill=$Fe&token=".get_token()."'); }, 1000 * $Ui);");}}ob_flush();flush();$J=@get_key_vals(($hi?:$H),$h,false);if($h){echo +script("clearTimeout(timeout);");ob_flush();flush();}return$J;}function +get_token(){$lh=rand(1,1e6);return($lh^$_SESSION["token"]).":$lh";}function +verify_token(){list($cj,$lh)=explode(":",$_POST["token"]);return($lh^$_SESSION["token"])==$cj;}function +lzw_decompress($Ka){$ec=256;$La=8;$ib=array();$yh=0;$zh=0;for($t=0;$t=$La){$zh-=$La;$ib[]=$yh>>$zh;$yh&=(1<<$zh)-1;$ec++;if($ec>>$La)$La++;}}$dc=range("\0","\xFF");$J="";$Zj="";foreach($ib +as$t=>$hb){$tc=$dc[$hb];if(!isset($tc))$tc=$Zj.$Zj[0];$J +.=$tc;if($t)$dc[]=$Zj.$tc[0];$Zj=$tc;}return$J;}function +script($ji,$fj="\n"){return"$ji$fj";}function +script_src($Dj,$Wb=false){return"\n";}function +nonce(){return' nonce="'.get_nonce().'"';}function +input_hidden($B,$Y=""){return"\n";}function +input_token(){return +input_hidden("token",get_token());}function +target_blank(){return' target="_blank" rel="noreferrer noopener"';}function +h($Q){return +str_replace("\0","�",htmlspecialchars($Q,ENT_QUOTES,'utf-8'));}function +nl_br($Q){return +str_replace("\n","
",$Q);}function +checkbox($B,$Y,$bb,$He="",$bg="",$fb="",$Je=""){$J="".($bg?script("qsl('input').onclick = function () { $bg };",""):"");return($He!=""||$fb?"$J".h($He)."":$J);}function +optionlist($gg,$Rh=null,$Hj=false){$J="";foreach($gg +as$Ce=>$W){$hg=array($Ce=>$W);if(is_array($W)){$J +.='';$hg=$W;}foreach($hg +as$y=>$X)$J +.=''.h($X);if(is_array($W))$J +.='';}return$J;}function +html_select($B,array$gg,$Y="",$ag="",$Je=""){static$He=0;$Ie="";if(!$Je&&substr($gg[""],0,1)=="("){$He++;$Je="label-$He";$Ie="

","$Qe",script("qsl('a').onclick = partial(toggle, 'fieldset-$u');",""),"","
\n";}function +selectSearchPrint(array$Z,array$e,array$x){print_fieldset("search",lang(57),$Z);foreach($x +as$t=>$w){if($w["type"]=="FULLTEXT")echo"
(".implode(", ",array_map('Adminer\h',$w["columns"])).") AGAINST"," ",script("qsl('input').oninput = selectFieldChange;",""),(JUSH=='sql'?checkbox("boolean[$t]",1,isset($_GET["boolean"][$t]),"BOOL"):''),"
\n";}$Va="this.parentNode.firstChild.onchange();";foreach(array_merge((array)$_GET["where"],array(array()))as$t=>$X){if(!$X||("$X[col]$X[val]"!=""&&in_array($X["op"],adminer()->operators())))echo"
".select_input(" name='where[$t][col]'",$e,$X["col"],($X?"selectFieldChange":"selectAddRow"),"(".lang(58).")"),html_select("where[$t][op]",adminer()->operators(),$X["op"],$Va),"",script("mixin(qsl('input'), {oninput: function () { $Va }, onkeydown: selectSearchKeydown, onsearch: selectSearchSearch});",""),"
\n";}echo"\n";}function +selectOrderPrint(array$ig,array$e,array$x){print_fieldset("sort",lang(59),$ig);$t=0;foreach((array)$_GET["order"]as$y=>$X){if($X!=""){echo"
".select_input(" name='order[$t]'",$e,$X,"selectFieldChange"),checkbox("desc[$t]",1,isset($_GET["desc"][$y]),lang(60))."
\n";$t++;}}echo"
".select_input(" name='order[$t]'",$e,"","selectAddRow"),checkbox("desc[$t]",1,false,lang(60))."
\n","\n";}function +selectLimitPrint($z){echo"
".lang(61)."
","",script("qsl('input').oninput = selectFieldChange;",""),"
\n";}function +selectLengthPrint($Si){if($Si!==null)echo"
".lang(62)."
","","
\n";}function +selectActionPrint(array$x){echo"
".lang(63)."
",""," ","\n","const indexColumns = ";$e=array();foreach($x +as$w){$Lb=reset($w["columns"]);if($w["type"]!="FULLTEXT"&&$Lb)$e[$Lb]=1;}$e[""]=1;foreach($e +as$y=>$X)json_row($y);echo";\n","selectFieldChange.call(qs('#form')['select']);\n","\n","
\n";}function +selectCommandPrint(){return!information_schema(DB);}function +selectImportPrint(){return!information_schema(DB);}function +selectEmailPrint(array$vc,array$e){}function +selectColumnsProcess(array$e,array$x){$M=array();$xd=array();foreach((array)$_GET["columns"]as$y=>$X){if($X["fun"]=="count"||($X["col"]!=""&&(!$X["fun"]||in_array($X["fun"],driver()->functions)||in_array($X["fun"],driver()->grouping)))){$M[$y]=apply_sql_function($X["fun"],($X["col"]!=""?idf_escape($X["col"]):"*"));if(!in_array($X["fun"],driver()->grouping))$xd[]=$M[$y];}}return +array($M,$xd);}function +selectSearchProcess(array$o,array$x){$J=array();foreach($x +as$t=>$w){if($w["type"]=="FULLTEXT"&&idx($_GET["fulltext"],$t)!="")$J[]="MATCH (".implode(", ",array_map('Adminer\idf_escape',$w["columns"])).") AGAINST (".q($_GET["fulltext"][$t]).(isset($_GET["boolean"][$t])?" IN BOOLEAN MODE":"").")";}foreach((array)$_GET["where"]as$y=>$X){$jb=$X["col"];if("$jb$X[val]"!=""&&in_array($X["op"],adminer()->operators())){$ub=array();foreach(($jb!=""?array($jb=>$o[$jb]):$o)as$B=>$n){$Xg="";$tb=" $X[op]";if(preg_match('~IN$~',$X["op"])){$Xd=process_length($X["val"]);$tb +.=" ".($Xd!=""?$Xd:"(NULL)");}elseif($X["op"]=="SQL")$tb=" $X[val]";elseif(preg_match('~^(I?LIKE) %%$~',$X["op"],$A))$tb=" $A[1] ".adminer()->processInput($n,"%$X[val]%");elseif($X["op"]=="FIND_IN_SET"){$Xg="$X[op](".q($X["val"]).", ";$tb=")";}elseif(!preg_match('~NULL$~',$X["op"]))$tb +.=" ".adminer()->processInput($n,$X["val"]);if($jb!=""||(isset($n["privileges"]["where"])&&(preg_match('~^[-\d.'.(preg_match('~IN$~',$X["op"])?',':'').']+$~',$X["val"])||!preg_match('~'.number_type().'|bit~',$n["type"]))&&(!preg_match("~[\x80-\xFF]~",$X["val"])||preg_match('~char|text|enum|set~',$n["type"]))&&(!preg_match('~date|timestamp~',$n["type"])||preg_match('~^\d+-\d+-\d+~',$X["val"]))))$ub[]=$Xg.driver()->convertSearch(idf_escape($B),$X,$n).$tb;}$J[]=(count($ub)==1?$ub[0]:($ub?"(".implode(" OR ",$ub).")":"1 = 0"));}}return$J;}function +selectOrderProcess(array$o,array$x){$J=array();foreach((array)$_GET["order"]as$y=>$X){if($X!="")$J[]=(preg_match('~^((COUNT\(DISTINCT |[A-Z0-9_]+\()(`(?:[^`]|``)+`|"(?:[^"]|"")+")\)|COUNT\(\*\))$~',$X)?$X:idf_escape($X)).(isset($_GET["desc"][$y])?" DESC":"");}return$J;}function +selectLimitProcess(){return(isset($_GET["limit"])?intval($_GET["limit"]):50);}function +selectLengthProcess(){return(isset($_GET["text_length"])?"$_GET[text_length]":"100");}function +selectEmailProcess(array$Z,array$ld){return +false;}function +selectQueryBuild(array$M,array$Z,array$xd,array$ig,$z,$D){return"";}function +messageQuery($H,$Ti,$Tc=false){restart_session();$Kd=&get_session("queries");if(!idx($Kd,$_GET["db"]))$Kd[$_GET["db"]]=array();if(strlen($H)>1e6)$H=preg_replace('~[\x80-\xFF]+$~','',substr($H,0,1e6))."\n…";$Kd[$_GET["db"]][]=array($H,time(),$Ti);$oi="sql-".count($Kd[$_GET["db"]]);$J="".lang(65)." 🗐\n";if(!$Tc&&($Vj=driver()->warnings())){$u="warnings-".count($Kd[$_GET["db"]]);$J="".lang(47).", $J\n";}return" ".@date("H:i:s").""." $J';}function +editRowPrint($R,array$o,$K,$_j){}function +editFunctions(array$n){$J=($n["null"]?"NULL/":"");$_j=isset($_GET["select"])||where($_GET);foreach(array(driver()->insertFunctions,driver()->editFunctions)as$y=>$sd){if(!$y||(!isset($_GET["call"])&&$_j)){foreach($sd +as$Lg=>$X){if(!$Lg||preg_match("~$Lg~",$n["type"]))$J +.="/$X";}}if($y&&$sd&&!preg_match('~set|bool~',$n["type"])&&!is_blob($n))$J +.="/SQL";}if($n["auto_increment"]&&!$_j)$J=lang(52);return +explode("/",$J);}function +editInput($R,array$n,$_a,$Y){if($n["type"]=="enum")return(isset($_GET["select"])?" ":"").enum_input("radio",$_a,$n,$Y,"NULL");return"";}function +editHint($R,array$n,$Y){return"";}function +processInput(array$n,$Y,$s=""){if($s=="SQL")return$Y;$B=$n["field"];$J=q($Y);if(preg_match('~^(now|getdate|uuid)$~',$s))$J="$s()";elseif(preg_match('~^current_(date|timestamp)$~',$s))$J=$s;elseif(preg_match('~^([+-]|\|\|)$~',$s))$J=idf_escape($B)." $s $J";elseif(preg_match('~^[+-] interval$~',$s))$J=idf_escape($B)." $s ".(preg_match("~^(\\d+|'[0-9.: -]') [A-Z_]+\$~i",$Y)&&JUSH!="pgsql"?$Y:$J);elseif(preg_match('~^(addtime|subtime|concat)$~',$s))$J="$s(".idf_escape($B).", $J)";elseif(preg_match('~^(md5|sha1|password|encrypt)$~',$s))$J="$s($J)";return +unconvert_field($n,$J);}function +dumpOutput(){$J=array('text'=>lang(66),'file'=>lang(67));if(function_exists('gzencode'))$J['gz']='gzip';return$J;}function +dumpFormat(){return(support("dump")?array('sql'=>'SQL'):array())+array('csv'=>'CSV,','csv;'=>'CSV;','tsv'=>'TSV');}function +dumpDatabase($k){}function +dumpTable($R,$wi,$ze=0){if($_POST["format"]!="sql"){echo"\xef\xbb\xbf";if($wi)dump_csv(array_keys(fields($R)));}else{if($ze==2){$o=array();foreach(fields($R)as$B=>$n)$o[]=idf_escape($B)." $n[full_type]";$i="CREATE TABLE ".table($R)." (".implode(", ",$o).")";}else$i=create_sql($R,$_POST["auto_increment"],$wi);set_utf8mb4($i);if($wi&&$i){if($wi=="DROP+CREATE"||$ze==1)echo"DROP ".($ze==2?"VIEW":"TABLE")." IF EXISTS ".table($R).";\n";if($ze==1)$i=remove_definer($i);echo"$i;\n\n";}}}function +dumpData($R,$wi,$H){if($wi){$hf=(JUSH=="sqlite"?0:1048576);$o=array();$Td=false;if($_POST["format"]=="sql"){if($wi=="TRUNCATE+INSERT")echo +truncate_sql($R).";\n";$o=fields($R);if(JUSH=="mssql"){foreach($o +as$n){if($n["auto_increment"]){echo"SET IDENTITY_INSERT ".table($R)." ON;\n";$Td=true;break;}}}}$I=connection()->query($H,1);if($I){$oe="";$Pa="";$Ee=array();$td=array();$yi="";$Wc=($R!=''?'fetch_assoc':'fetch_row');$Eb=0;while($K=$I->$Wc()){if(!$Ee){$Nj=array();foreach($K +as$X){$n=$I->fetch_field();if(idx($o[$n->name],'generated')){$td[$n->name]=true;continue;}$Ee[]=$n->name;$y=idf_escape($n->name);$Nj[]="$y = VALUES($y)";}$yi=($wi=="INSERT+UPDATE"?"\nON DUPLICATE KEY UPDATE ".implode(", ",$Nj):"").";\n";}if($_POST["format"]!="sql"){if($wi=="table"){dump_csv($Ee);$wi="INSERT";}dump_csv($K);}else{if(!$oe)$oe="INSERT INTO ".table($R)." (".implode(", ",array_map('Adminer\idf_escape',$Ee)).") VALUES";foreach($K +as$y=>$X){if($td[$y]){unset($K[$y]);continue;}$n=$o[$y];$K[$y]=($X!==null?unconvert_field($n,preg_match(number_type(),$n["type"])&&!preg_match('~\[~',$n["full_type"])&&is_numeric($X)?$X:q(($X===false?0:$X))):"NULL");}$Hh=($hf?"\n":" ")."(".implode(",\t",$K).")";if(!$Pa)$Pa=$oe.$Hh;elseif(JUSH=='mssql'?$Eb%1000!=0:strlen($Pa)+4+strlen($Hh)+strlen($yi)<$hf)$Pa +.=",$Hh";else{echo$Pa.$yi;$Pa=$oe.$Hh;}}$Eb++;}if($Pa)echo$Pa.$yi;}elseif($_POST["format"]=="sql")echo"-- ".str_replace("\n"," ",connection()->error)."\n";if($Td)echo"SET IDENTITY_INSERT ".table($R)." OFF;\n";}}function +dumpFilename($Rd){return +friendly_url($Rd!=""?$Rd:(SERVER?:"localhost"));}function +dumpHeaders($Rd,$Af=false){$vg=$_POST["output"];$Oc=(preg_match('~sql~',$_POST["format"])?"sql":($Af?"tar":"csv"));header("Content-Type: ".($vg=="gz"?"application/x-gzip":($Oc=="tar"?"application/x-tar":($Oc=="sql"||$vg!="file"?"text/plain":"text/csv")."; charset=utf-8")));if($vg=="gz"){ob_start(function($Q){return +gzencode($Q);},1e6);}return$Oc;}function +dumpFooter(){if($_POST["format"]=="sql")echo"-- ".gmdate("Y-m-d H:i:s e")."\n";}function +importServerPath(){return"adminer.sql";}function +homepage(){echo'

".adminer()->name()." ".VERSION;$If=$_COOKIE["adminer_version"];echo" ".(version_compare(VERSION,$If)<0?h($If):"")."","

\n";switch_lang();if($xf=="auth"){$vg="";foreach((array)$_SESSION["pwds"]as$Pj=>$ai){foreach($ai +as$N=>$Jj){$B=h(get_setting("vendor-$Pj-$N")?:get_driver($Pj));foreach($Jj +as$V=>$F){if($F!==null){$Sb=$_SESSION["db"][$Pj][$N][$V];foreach(($Sb?array_keys($Sb):array(""))as$k)$vg +.="
  • ($B) ".h("$V@".($N!=""?adminer()->serverName($N):"").($k!=""?" - $k":""))."\n";}}}}if($vg)echo"\n".script("mixin(qs('#logins'), {onmouseover: menuOver, onmouseout: menuOut});");}else{$T=array();if($_GET["ns"]!==""&&!$xf&&DB!=""){connection()->select_db(DB);$T=table_status('',true);}adminer()->syntaxHighlighting($T);adminer()->databasesPrint($xf);$ka=array();if(DB==""||!$xf){if(support("sql")){$ka[]="".lang(65)."";$ka[]="".lang(76)."";}$ka[]="".lang(77)."";}$Yd=$_GET["ns"]!==""&&!$xf&&DB!="";if($Yd&&function_exists('Adminer\alter_table'))$ka[]='".lang(78)."";echo($ka?"

    ".lang(11)."

    \n";}}}function +syntaxHighlighting(array$T){echo +script_src(preg_replace("~\\?.*~","",ME)."?file=jush.js&version=5.4.2",true);if(support("sql")){echo"\n";if($T){$Ve=array();foreach($T +as$R=>$U)$Ve[]=preg_quote($R,'/');echo"var jushLinks = { ".JUSH.":";json_row(js_escape(ME).(support("table")?"table":"select").'=$&','/\b('.implode('|',$Ve).')\b/g',false);if(support('routine')){foreach(routines()as$K)json_row(js_escape(ME).'function='.urlencode($K["SPECIFIC_NAME"]).'&name=$&','/\b'.preg_quote($K["ROUTINE_NAME"],'/').'(?=["`]?\()/g',false);}json_row('');echo"};\n";foreach(array("bac","bra","sqlite_quo","mssql_bra")as$X)echo"jushLinks.$X = jushLinks.".JUSH.";\n";if(isset($_GET["sql"])||isset($_GET["trigger"])||isset($_GET["check"])){$Ii=array_fill_keys(array_keys($T),array());foreach(driver()->allFields()as$R=>$o){foreach($o +as$n)$Ii[$R][]=$n["field"];}echo"addEventListener('DOMContentLoaded', () => { autocompleter = jush.autocompleteSql('".idf_escape("")."', ".json_encode($Ii)."); });\n";}}echo"\n";}echo +script("syntaxHighlighting('".preg_replace('~^(\d\.?\d).*~s','\1',connection()->server_info)."', '".connection()->flavor."');");}function +databasesPrint($xf){$j=adminer()->databases();if(DB&&$j&&!in_array(DB,$j))array_unshift($j,DB);echo"
    \n

    \n";hidden_fields_get();$Qb=script("mixin(qsl('select'), {onmousedown: dbMouseDown, onchange: dbChange});");echo"","\n";if(support("scheme")){if($xf!="db"&&DB!=""&&connection()->select_db(DB)){echo"
    ";if($_GET["ns"]!="")set_schema($_GET["ns"]);}}foreach(array("import","sql","schema","dump","privileges")as$X){if(isset($_GET[$X])){echo +input_hidden($X);break;}}echo"

    \n";}function +tablesPrint(array$T){echo"\n";}function +showVariables(){return +show_variables();}function +showStatus(){return +show_status();}function +processList(){return +process_list();}function +killProcess($u){return +kill_process($u);}}class +Plugins{private +static$append=array('dumpFormat'=>true,'dumpOutput'=>true,'editRowPrint'=>true,'editFunctions'=>true,'config'=>true);var$plugins;var$error='';private$hooks=array();function +__construct($Qg){if($Qg===null){$Qg=array();$Ja="adminer-plugins";if(is_dir($Ja)){foreach(glob("$Ja/*.php")as$p)$this->includeOnce($p);}$Jd=" href='https://www.adminer.org/plugins/#use'".target_blank();if(file_exists("$Ja.php")){$Zd=$this->includeOnce("$Ja.php");if(is_array($Zd)){foreach($Zd +as$Pg)$Qg[get_class($Pg)]=$Pg;}else$this->error +.=lang(82,"$Ja.php",$Jd)."
    ";}foreach(get_declared_classes()as$fb){if(!$Qg[$fb]&&(preg_match('~^Adminer\w~i',$fb)||is_subclass_of($fb,'Adminer\Plugin'))){$sh=new +\ReflectionClass($fb);$zb=$sh->getConstructor();if($zb&&$zb->getNumberOfRequiredParameters())$this->error +.=lang(83,$Jd,"$fb","$Ja.php")."
    ";else$Qg[$fb]=new$fb;}}}$this->plugins=$Qg;$na=new +Adminer;$Qg[]=$na;$sh=new +\ReflectionObject($na);foreach($sh->getMethods()as$vf){foreach($Qg +as$Pg){$B=$vf->getName();if(method_exists($Pg,$B))$this->hooks[$B][]=$Pg;}}}function +includeOnce($p){return +include_once"./$p";}function +__call($B,array$_g){$wa=array();foreach($_g +as$y=>$X)$wa[]=&$_g[$y];$J=null;foreach($this->hooks[$B]as$Pg){$Y=call_user_func_array(array($Pg,$B),$wa);if($Y!==null){if(!self::$append[$B])return$Y;$J=$Y+(array)$J;}}return$J;}}abstract +class +Plugin{protected$translations=array();function +description(){return$this->lang('');}function +screenshot(){return"";}protected +function +lang($v,$Of=null){$wa=func_get_args();$wa[0]=idx($this->translations[LANG],$v)?:$v;return +call_user_func_array('Adminer\lang_format',$wa);}}Adminer::$instance=(function_exists('adminer_object')?adminer_object():(is_dir("adminer-plugins")||file_exists("adminer-plugins.php")?new +Plugins(null):new +Adminer));SqlDriver::$drivers=array("server"=>"MySQL / MariaDB")+SqlDriver::$drivers;if(!defined('Adminer\DRIVER')){define('Adminer\DRIVER',"server");if(extension_loaded("mysqli")&&$_GET["ext"]!="pdo"){class +Db +extends +\MySQLi{static$instance;var$extension="MySQLi",$flavor='';function +__construct(){parent::init();}function +attach($N,$V,$F){mysqli_report(MYSQLI_REPORT_OFF);list($Nd,$Rg)=host_port($N);$ri=adminer()->connectSsl();if($ri)$this->ssl_set($ri['key'],$ri['cert'],$ri['ca'],'','');$J=@$this->real_connect(($N!=""?$Nd:ini_get("mysqli.default_host")),($N.$V!=""?$V:ini_get("mysqli.default_user")),($N.$V.$F!=""?$F:ini_get("mysqli.default_pw")),null,(is_numeric($Rg)?intval($Rg):ini_get("mysqli.default_port")),(is_numeric($Rg)?null:$Rg),($ri?($ri['verify']!==false?2048:64):0));$this->options(MYSQLI_OPT_LOCAL_INFILE,0);return($J?'':$this->error);}function +set_charset($Xa){if(parent::set_charset($Xa))return +true;parent::set_charset('utf8');return$this->query("SET NAMES $Xa");}function +next_result(){return +self::more_results()&&parent::next_result();}function +quote($Q){return"'".$this->escape_string($Q)."'";}}}elseif(extension_loaded("mysql")&&!((ini_bool("sql.safe_mode")||ini_bool("mysql.allow_local_infile"))&&extension_loaded("pdo_mysql"))){class +Db +extends +SqlDb{private$link;function +attach($N,$V,$F){if(ini_bool("mysql.allow_local_infile"))return +lang(84,"'mysql.allow_local_infile'","MySQLi","PDO_MySQL");$this->link=@mysql_connect(($N!=""?$N:ini_get("mysql.default_host")),($N.$V!=""?$V:ini_get("mysql.default_user")),($N.$V.$F!=""?$F:ini_get("mysql.default_password")),true,131072);if(!$this->link)return +mysql_error();$this->server_info=mysql_get_server_info($this->link);return'';}function +set_charset($Xa){if(function_exists('mysql_set_charset')){if(mysql_set_charset($Xa,$this->link))return +true;mysql_set_charset('utf8',$this->link);}return$this->query("SET NAMES $Xa");}function +quote($Q){return"'".mysql_real_escape_string($Q,$this->link)."'";}function +select_db($Pb){return +mysql_select_db($Pb,$this->link);}function +query($H,$sj=false){$I=@($sj?mysql_unbuffered_query($H,$this->link):mysql_query($H,$this->link));$this->error="";if(!$I){$this->errno=mysql_errno($this->link);$this->error=mysql_error($this->link);return +false;}if($I===true){$this->affected_rows=mysql_affected_rows($this->link);$this->info=mysql_info($this->link);return +true;}return +new +Result($I);}}class +Result{var$num_rows;private$result;private$offset=0;function +__construct($I){$this->result=$I;$this->num_rows=mysql_num_rows($I);}function +fetch_assoc(){return +mysql_fetch_assoc($this->result);}function +fetch_row(){return +mysql_fetch_row($this->result);}function +fetch_field(){$J=mysql_fetch_field($this->result,$this->offset++);$J->orgtable=$J->table;$J->charsetnr=($J->blob?63:0);return$J;}function +__destruct(){mysql_free_result($this->result);}}}elseif(extension_loaded("pdo_mysql")){class +Db +extends +PdoDb{var$extension="PDO_MySQL";function +attach($N,$V,$F){$gg=array(\PDO::MYSQL_ATTR_LOCAL_INFILE=>false);$ri=adminer()->connectSsl();if($ri){if($ri['key'])$gg[\PDO::MYSQL_ATTR_SSL_KEY]=$ri['key'];if($ri['cert'])$gg[\PDO::MYSQL_ATTR_SSL_CERT]=$ri['cert'];if($ri['ca'])$gg[\PDO::MYSQL_ATTR_SSL_CA]=$ri['ca'];if(isset($ri['verify']))$gg[\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT]=$ri['verify'];}list($Nd,$Rg)=host_port($N);return$this->dsn("mysql:charset=utf8;host=$Nd".($Rg?(is_numeric($Rg)?";port=":";unix_socket=").$Rg:""),$V,$F,$gg);}function +set_charset($Xa){return$this->query("SET NAMES $Xa");}function +select_db($Pb){return$this->query("USE ".idf_escape($Pb));}function +query($H,$sj=false){$this->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,!$sj);return +parent::query($H,$sj);}}}class +Driver +extends +SqlDriver{static$extensions=array("MySQLi","MySQL","PDO_MySQL");static$jush="sql";var$unsigned=array("unsigned","zerofill","unsigned zerofill");var$operators=array("=","<",">","<=",">=","!=","LIKE","LIKE %%","REGEXP","IN","FIND_IN_SET","IS NULL","NOT LIKE","NOT REGEXP","NOT IN","IS NOT NULL","SQL");var$functions=array("char_length","date","from_unixtime","lower","round","floor","ceil","sec_to_time","time_to_sec","upper");var$grouping=array("avg","count","count distinct","group_concat","max","min","sum");static +function +connect($N,$V,$F){$g=parent::connect($N,$V,$F);if(is_string($g)){if(function_exists('iconv')&&!is_utf8($g)&&strlen($Hh=iconv("windows-1250","utf-8",$g))>strlen($g))$g=$Hh;return$g;}$g->set_charset(charset($g));$g->query("SET sql_quote_show_create = 1, autocommit = 1");$g->flavor=(preg_match('~MariaDB~',$g->server_info)?'maria':'mysql');add_driver(DRIVER,($g->flavor=='maria'?"MariaDB":"MySQL"));return$g;}function +__construct(Db$g){parent::__construct($g);$this->types=array(lang(27)=>array("tinyint"=>3,"smallint"=>5,"mediumint"=>8,"int"=>10,"bigint"=>20,"decimal"=>66,"float"=>12,"double"=>21),lang(28)=>array("date"=>10,"datetime"=>19,"timestamp"=>19,"time"=>10,"year"=>4),lang(29)=>array("char"=>255,"varchar"=>65535,"tinytext"=>255,"text"=>65535,"mediumtext"=>16777215,"longtext"=>4294967295),lang(85)=>array("enum"=>65535,"set"=>64),lang(30)=>array("bit"=>20,"binary"=>255,"varbinary"=>65535,"tinyblob"=>255,"blob"=>65535,"mediumblob"=>16777215,"longblob"=>4294967295),lang(32)=>array("geometry"=>0,"point"=>0,"linestring"=>0,"polygon"=>0,"multipoint"=>0,"multilinestring"=>0,"multipolygon"=>0,"geometrycollection"=>0),);$this->insertFunctions=array("char"=>"md5/sha1/password/encrypt/uuid","binary"=>"md5/sha1","date|time"=>"now",);$this->editFunctions=array(number_type()=>"+/-","date"=>"+ interval/- interval","time"=>"addtime/subtime","char|text"=>"concat",);if(min_version('5.7.8',10.2,$g))$this->types[lang(29)]["json"]=4294967295;if(min_version('',10.7,$g)){$this->types[lang(29)]["uuid"]=128;$this->insertFunctions['uuid']='uuid';}if(min_version(9,'',$g)){$this->types[lang(27)]["vector"]=16383;$this->insertFunctions['vector']='string_to_vector';}if(min_version(5.1,'',$g))$this->partitionBy=array("HASH","LINEAR HASH","KEY","LINEAR KEY","RANGE","LIST");if(min_version(5.7,10.2,$g))$this->generated=array("STORED","VIRTUAL");}function +unconvertFunction(array$n){return(preg_match("~binary~",$n["type"])?"UNHEX":($n["type"]=="bit"?doc_link(array('sql'=>'bit-value-literals.html'),"b''"):(preg_match("~geometry|point|linestring|polygon~",$n["type"])?"GeomFromText":"")));}function +insert($R,array$O){return($O?parent::insert($R,$O):queries("INSERT INTO ".table($R)." ()\nVALUES ()"));}function +insertUpdate($R,array$L,array$G){$e=array_keys(reset($L));$Xg="INSERT INTO ".table($R)." (".implode(", ",$e).") VALUES\n";$Nj=array();foreach($e +as$y)$Nj[$y]="$y = VALUES($y)";$yi="\nON DUPLICATE KEY UPDATE ".implode(", ",$Nj);$Nj=array();$Re=0;foreach($L +as$O){$Y="(".implode(", ",$O).")";if($Nj&&(strlen($Xg)+$Re+strlen($Y)+strlen($yi)>1e6)){if(!queries($Xg.implode(",\n",$Nj).$yi))return +false;$Nj=array();$Re=0;}$Nj[]=$Y;$Re+=strlen($Y)+2;}return +queries($Xg.implode(",\n",$Nj).$yi);}function +slowQuery($H,$Ui){if(min_version('5.7.8','10.1.2')){if($this->conn->flavor=='maria')return"SET STATEMENT max_statement_time=$Ui FOR $H";elseif(preg_match('~^(SELECT\b)(.+)~is',$H,$A))return"$A[1] /*+ MAX_EXECUTION_TIME(".($Ui*1000).") */ $A[2]";}}function +convertSearch($v,array$X,array$n){return(preg_match('~char|text|enum|set~',$n["type"])&&!preg_match("~^utf8~",$n["collation"])&&preg_match('~[\x80-\xFF]~',$X['val'])?"CONVERT($v USING ".charset($this->conn).")":$v);}function +warnings(){$I=$this->conn->query("SHOW WARNINGS");if($I&&$I->num_rows){ob_start();print_select_result($I);return +ob_get_clean();}}function +tableHelp($B,$ze=false){$Ze=($this->conn->flavor=='maria');if(information_schema(DB))return +strtolower("information-schema-".($Ze?"$B-table/":str_replace("_","-",$B)."-table.html"));if(DB=="mysql")return($Ze?"mysql$B-table/":"system-schema.html");}function +partitionsInfo($R){$qd="FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = ".q(DB)." AND TABLE_NAME = ".q($R);$I=$this->conn->query("SELECT PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_ORDINAL_POSITION $qd ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");$J=array();list($J["partition_by"],$J["partition"],$J["partitions"])=$I->fetch_row();$Hg=get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $qd AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION");$J["partition_names"]=array_keys($Hg);$J["partition_values"]=array_values($Hg);return$J;}function +hasCStyleEscapes(){static$Sa;if($Sa===null){$pi=get_val("SHOW VARIABLES LIKE 'sql_mode'",1,$this->conn);$Sa=(strpos($pi,'NO_BACKSLASH_ESCAPES')===false);}return$Sa;}function +engines(){$J=array();foreach(get_rows("SHOW ENGINES")as$K){if(preg_match("~YES|DEFAULT~",$K["Support"]))$J[]=$K["Engine"];}return$J;}function +indexAlgorithms(array$Bi){return(preg_match('~^(MEMORY|NDB)$~',$Bi["Engine"])?array("HASH","BTREE"):array());}}function +idf_escape($v){return"`".str_replace("`","``",$v)."`";}function +table($v){return +idf_escape($v);}function +get_databases($id){$J=get_session("dbs");if($J===null){$H="SELECT SCHEMA_NAME FROM information_schema.SCHEMATA ORDER BY SCHEMA_NAME";$J=($id?slow_query($H):get_vals($H));restart_session();set_session("dbs",$J);stop_session();}return$J;}function +limit($H,$Z,$z,$C=0,$Vh=" "){return" $H$Z".($z?$Vh."LIMIT $z".($C?" OFFSET $C":""):"");}function +limit1($R,$H,$Z,$Vh="\n"){return +limit($H,$Z,1,0,$Vh);}function +db_collation($k,array$lb){$J=null;$i=get_val("SHOW CREATE DATABASE ".idf_escape($k),1);if(preg_match('~ COLLATE ([^ ]+)~',$i,$A))$J=$A[1];elseif(preg_match('~ CHARACTER SET ([^ ]+)~',$i,$A))$J=$lb[$A[1]][-1];return$J;}function +logged_user(){return +get_val("SELECT USER()");}function +tables_list(){return +get_key_vals("SELECT TABLE_NAME, TABLE_TYPE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME");}function +count_tables(array$j){$J=array();foreach($j +as$k)$J[$k]=count(get_vals("SHOW TABLES IN ".idf_escape($k)));return$J;}function +table_status($B="",$Uc=false){$J=array();foreach(get_rows($Uc?"SELECT TABLE_NAME AS Name, ENGINE AS Engine, TABLE_COMMENT AS Comment FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() ".($B!=""?"AND TABLE_NAME = ".q($B):"ORDER BY Name"):"SHOW TABLE STATUS".($B!=""?" LIKE ".q(addcslashes($B,"%_\\")):""))as$K){if($K["Engine"]=="InnoDB")$K["Comment"]=preg_replace('~(?:(.+); )?InnoDB free: .*~','\1',$K["Comment"]);if(!isset($K["Engine"]))$K["Comment"]="";if($B!="")$K["Name"]=$B;$J[$K["Name"]]=$K;}return$J;}function +is_view(array$S){return$S["Engine"]===null;}function +fk_support(array$S){return +preg_match('~InnoDB|IBMDB2I'.(min_version(5.6)?'|NDB':'').'~i',$S["Engine"]);}function +fields($R){$Ze=(connection()->flavor=='maria');$J=array();foreach(get_rows("SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ".q($R)." ORDER BY ORDINAL_POSITION")as$K){$n=$K["COLUMN_NAME"];$U=$K["COLUMN_TYPE"];$ud=$K["GENERATION_EXPRESSION"];$Rc=$K["EXTRA"];preg_match('~^(VIRTUAL|PERSISTENT|STORED)~',$Rc,$td);preg_match('~^([^( ]+)(?:\((.+)\))?( unsigned)?( zerofill)?$~',$U,$cf);$l=$K["COLUMN_DEFAULT"];if($l!=""){$ye=preg_match('~text|json~',$cf[1]);if(!$Ze&&$ye)$l=preg_replace("~^(_\w+)?('.*')$~",'\2',stripslashes($l));if($Ze||$ye){$l=($l=="NULL"?null:preg_replace_callback("~^'(.*)'$~",function($A){return +stripslashes(str_replace("''","'",$A[1]));},$l));}if(!$Ze&&preg_match('~binary~',$cf[1])&&preg_match('~^0x(\w*)$~',$l,$A))$l=pack("H*",$A[1]);}$J[$n]=array("field"=>$n,"full_type"=>$U,"type"=>$cf[1],"length"=>$cf[2],"unsigned"=>ltrim($cf[3].$cf[4]),"default"=>($td?($Ze?$ud:stripslashes($ud)):$l),"null"=>($K["IS_NULLABLE"]=="YES"),"auto_increment"=>($Rc=="auto_increment"),"on_update"=>(preg_match('~\bon update (\w+)~i',$Rc,$A)?$A[1]:""),"collation"=>$K["COLLATION_NAME"],"privileges"=>array_flip(explode(",","$K[PRIVILEGES],where,order")),"comment"=>$K["COLUMN_COMMENT"],"primary"=>($K["COLUMN_KEY"]=="PRI"),"generated"=>($td[1]=="PERSISTENT"?"STORED":$td[1]),);}return$J;}function +indexes($R,$h=null){$J=array();foreach(get_rows("SHOW INDEX FROM ".table($R),$h)as$K){$B=$K["Key_name"];$J[$B]["type"]=($B=="PRIMARY"?"PRIMARY":($K["Index_type"]=="FULLTEXT"?"FULLTEXT":($K["Non_unique"]?($K["Index_type"]=="SPATIAL"?"SPATIAL":"INDEX"):"UNIQUE")));$J[$B]["columns"][]=$K["Column_name"];$J[$B]["lengths"][]=($K["Index_type"]=="SPATIAL"?null:$K["Sub_part"]);$J[$B]["descs"][]=null;$J[$B]["algorithm"]=$K["Index_type"];}return$J;}function +foreign_keys($R){static$Lg='(?:`(?:[^`]|``)+`|"(?:[^"]|"")+")';$J=array();$Fb=get_val("SHOW CREATE TABLE ".table($R),1);if($Fb){preg_match_all("~CONSTRAINT ($Lg) FOREIGN KEY ?\\(((?:$Lg,? ?)+)\\) REFERENCES ($Lg)(?:\\.($Lg))? \\(((?:$Lg,? ?)+)\\)(?: ON DELETE (".driver()->onActions."))?(?: ON UPDATE (".driver()->onActions."))?~",$Fb,$df,PREG_SET_ORDER);foreach($df +as$A){preg_match_all("~$Lg~",$A[2],$ji);preg_match_all("~$Lg~",$A[5],$Mi);$J[idf_unescape($A[1])]=array("db"=>idf_unescape($A[4]!=""?$A[3]:$A[4]),"table"=>idf_unescape($A[4]!=""?$A[4]:$A[3]),"source"=>array_map('Adminer\idf_unescape',$ji[0]),"target"=>array_map('Adminer\idf_unescape',$Mi[0]),"on_delete"=>($A[6]?:"RESTRICT"),"on_update"=>($A[7]?:"RESTRICT"),);}}return$J;}function +view($B){return +array("select"=>preg_replace('~^(?:[^`]|`[^`]*`)*\s+AS\s+~isU','',get_val("SHOW CREATE VIEW ".table($B),1)));}function +collations(){$J=array();foreach(get_rows("SHOW COLLATION")as$K){if($K["Default"])$J[$K["Charset"]][-1]=$K["Collation"];else$J[$K["Charset"]][]=$K["Collation"];}ksort($J);foreach($J +as$y=>$X)sort($J[$y]);return$J;}function +information_schema($k){return($k=="information_schema")||(min_version(5.5)&&$k=="performance_schema");}function +error(){return +h(preg_replace('~^You have an error.*syntax to use~U',"Syntax error",connection()->error));}function +create_database($k,$c){return +queries("CREATE DATABASE ".idf_escape($k).($c?" COLLATE ".q($c):""));}function +drop_databases(array$j){$J=apply_queries("DROP DATABASE",$j,'Adminer\idf_escape');restart_session();set_session("dbs",null);return$J;}function +rename_database($B,$c){$J=false;if(create_database($B,$c)){$T=array();$Sj=array();foreach(tables_list()as$R=>$U){if($U=='VIEW')$Sj[]=$R;else$T[]=$R;}$J=(!$T&&!$Sj)||move_tables($T,$Sj,$B);drop_databases($J?array(DB):array());}return$J;}function +auto_increment(){$Ca=" PRIMARY KEY";if($_GET["create"]!=""&&$_POST["auto_increment_col"]){foreach(indexes($_GET["create"])as$w){if(in_array($_POST["fields"][$_POST["auto_increment_col"]]["orig"],$w["columns"],true)){$Ca="";break;}if($w["type"]=="PRIMARY")$Ca=" UNIQUE";}}return" AUTO_INCREMENT$Ca";}function +alter_table($R,$B,array$o,array$kd,$qb,$yc,$c,$Ba,$E){$b=array();foreach($o +as$n){if($n[1]){$l=$n[1][3];if(preg_match('~ GENERATED~',$l)){$n[1][3]=(connection()->flavor=='maria'?"":$n[1][2]);$n[1][2]=$l;}$b[]=($R!=""?($n[0]!=""?"CHANGE ".idf_escape($n[0]):"ADD"):" ")." ".implode($n[1]).($R!=""?$n[2]:"");}else$b[]="DROP ".idf_escape($n[0]);}$b=array_merge($b,$kd);$P=($qb!==null?" COMMENT=".q($qb):"").($yc?" ENGINE=".q($yc):"").($c?" COLLATE ".q($c):"").($Ba!=""?" AUTO_INCREMENT=$Ba":"");if($E){$Hg=array();if($E["partition_by"]=='RANGE'||$E["partition_by"]=='LIST'){foreach($E["partition_names"]as$y=>$X){$Y=$E["partition_values"][$y];$Hg[]="\n PARTITION ".idf_escape($X)." VALUES ".($E["partition_by"]=='RANGE'?"LESS THAN":"IN").($Y!=""?" ($Y)":" MAXVALUE");}}$P +.="\nPARTITION BY $E[partition_by]($E[partition])";if($Hg)$P +.=" (".implode(",",$Hg)."\n)";elseif($E["partitions"])$P +.=" PARTITIONS ".(+$E["partitions"]);}elseif($E===null)$P +.="\nREMOVE PARTITIONING";if($R=="")return +queries("CREATE TABLE ".table($B)." (\n".implode(",\n",$b)."\n)$P");if($R!=$B)$b[]="RENAME TO ".table($B);if($P)$b[]=ltrim($P);return($b?queries("ALTER TABLE ".table($R)."\n".implode(",\n",$b)):true);}function +alter_indexes($R,$b){$Wa=array();foreach($b +as$X)$Wa[]=($X[2]=="DROP"?"\nDROP INDEX ".idf_escape($X[1]):"\nADD $X[0] ".($X[0]=="PRIMARY"?"KEY ":"").($X[1]!=""?idf_escape($X[1])." ":"")."(".implode(", ",$X[2]).")");return +queries("ALTER TABLE ".table($R).implode(",",$Wa));}function +truncate_tables(array$T){return +apply_queries("TRUNCATE TABLE",$T);}function +drop_views(array$Sj){return +queries("DROP VIEW ".implode(", ",array_map('Adminer\table',$Sj)));}function +drop_tables(array$T){return +queries("DROP TABLE ".implode(", ",array_map('Adminer\table',$T)));}function +move_tables(array$T,array$Sj,$Mi){$wh=array();foreach($T +as$R)$wh[]=table($R)." TO ".idf_escape($Mi).".".table($R);if(!$wh||queries("RENAME TABLE ".implode(", ",$wh))){$Yb=array();foreach($Sj +as$R)$Yb[table($R)]=view($R);connection()->select_db($Mi);$k=idf_escape(DB);foreach($Yb +as$B=>$Rj){if(!queries("CREATE VIEW $B AS ".str_replace(" $k."," ",$Rj["select"]))||!queries("DROP VIEW $k.$B"))return +false;}return +true;}return +false;}function +copy_tables(array$T,array$Sj,$Mi){queries("SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'");foreach($T +as$R){$B=($Mi==DB?table("copy_$R"):idf_escape($Mi).".".table($R));if(($_POST["overwrite"]&&!queries("\nDROP TABLE IF EXISTS $B"))||!queries("CREATE TABLE $B LIKE ".table($R))||!queries("INSERT INTO $B SELECT * FROM ".table($R)))return +false;foreach(get_rows("SHOW TRIGGERS LIKE ".q(addcslashes($R,"%_\\")))as$K){$lj=$K["Trigger"];if(!queries("CREATE TRIGGER ".($Mi==DB?idf_escape("copy_$lj"):idf_escape($Mi).".".idf_escape($lj))." $K[Timing] $K[Event] ON $B FOR EACH ROW\n$K[Statement];"))return +false;}}foreach($Sj +as$R){$B=($Mi==DB?table("copy_$R"):idf_escape($Mi).".".table($R));$Rj=view($R);if(($_POST["overwrite"]&&!queries("DROP VIEW IF EXISTS $B"))||!queries("CREATE VIEW $B AS $Rj[select]"))return +false;}return +true;}function +trigger($B,$R){if($B=="")return +array();$L=get_rows("SHOW TRIGGERS WHERE `Trigger` = ".q($B));return +reset($L);}function +triggers($R){$J=array();foreach(get_rows("SHOW TRIGGERS LIKE ".q(addcslashes($R,"%_\\")))as$K)$J[$K["Trigger"]]=array($K["Timing"],$K["Event"]);return$J;}function +trigger_options(){return +array("Timing"=>array("BEFORE","AFTER"),"Event"=>array("INSERT","UPDATE","DELETE"),"Type"=>array("FOR EACH ROW"),);}function +routine($B,$U){$o=get_rows("SELECT + PARAMETER_NAME field, + DATA_TYPE type, + CHARACTER_MAXIMUM_LENGTH length, + REGEXP_REPLACE(DTD_IDENTIFIER, '^[^ ]+ ', '') `unsigned`, + 1 `null`, + DTD_IDENTIFIER full_type, + PARAMETER_MODE `inout`, + CHARACTER_SET_NAME collation +FROM information_schema.PARAMETERS +WHERE SPECIFIC_SCHEMA = DATABASE() AND ROUTINE_TYPE = '$U' AND SPECIFIC_NAME = ".q($B)." +ORDER BY ORDINAL_POSITION");$J=connection()->query("SELECT ROUTINE_COMMENT comment, ROUTINE_DEFINITION definition, 'SQL' language +FROM information_schema.ROUTINES +WHERE ROUTINE_SCHEMA = DATABASE() AND ROUTINE_TYPE = '$U' AND ROUTINE_NAME = ".q($B))->fetch_assoc();if($o&&$o[0]['field']=='')$J['returns']=array_shift($o);$J['fields']=$o;return$J;}function +routines(){return +get_rows("SELECT SPECIFIC_NAME, ROUTINE_NAME, ROUTINE_TYPE, DTD_IDENTIFIER FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE()");}function +routine_languages(){return +array();}function +routine_id($B,array$K){return +idf_escape($B);}function +last_id($I){return +get_val("SELECT LAST_INSERT_ID()");}function +explain(Db$g,$H){return$g->query("EXPLAIN ".(min_version(5.1)&&!min_version(5.7)?"PARTITIONS ":"").$H);}function +found_rows(array$S,array$Z){return($Z||$S["Engine"]!="InnoDB"?null:$S["Rows"]);}function +create_sql($R,$Ba,$wi){$J=get_val("SHOW CREATE TABLE ".table($R),1);if(!$Ba)$J=preg_replace('~ AUTO_INCREMENT=\d+~','',$J);return$J;}function +truncate_sql($R){return"TRUNCATE ".table($R);}function +use_sql($Pb,$wi=""){$B=idf_escape($Pb);$J="";if(preg_match('~CREATE~',$wi)&&($i=get_val("SHOW CREATE DATABASE $B",1))){set_utf8mb4($i);if($wi=="DROP+CREATE")$J="DROP DATABASE IF EXISTS $B;\n";$J +.="$i;\n";}return$J."USE $B";}function +trigger_sql($R){$J="";foreach(get_rows("SHOW TRIGGERS LIKE ".q(addcslashes($R,"%_\\")),null,"-- ")as$K)$J +.="\nCREATE TRIGGER ".idf_escape($K["Trigger"])." $K[Timing] $K[Event] ON ".table($K["Table"])." FOR EACH ROW\n$K[Statement];;\n";return$J;}function +show_variables(){return +get_rows("SHOW VARIABLES");}function +show_status(){return +get_rows("SHOW STATUS");}function +process_list(){return +get_rows("SHOW FULL PROCESSLIST");}function +convert_field(array$n){if(preg_match("~binary~",$n["type"]))return"HEX(".idf_escape($n["field"]).")";if($n["type"]=="bit")return"BIN(".idf_escape($n["field"])." + 0)";if(preg_match("~geometry|point|linestring|polygon~",$n["type"]))return(min_version(8)?"ST_":"")."AsWKT(".idf_escape($n["field"]).")";}function +unconvert_field(array$n,$J){if(preg_match("~binary~",$n["type"]))$J="UNHEX($J)";if($n["type"]=="bit")$J="CONVERT(b$J, UNSIGNED)";if(preg_match("~geometry|point|linestring|polygon~",$n["type"])){$Xg=(min_version(8)?"ST_":"");$J=$Xg."GeomFromText($J, $Xg"."SRID($n[field]))";}return$J;}function +support($Vc){return +preg_match('~^(comment|columns|copy|database|drop_col|dump|indexes|kill|privileges|move_col|procedure|processlist|routine|sql|status|table|trigger|variables|view'.(min_version(5.1)?'|event':'').(min_version(8)?'|descidx':'').(min_version('8.0.16','10.2.1')?'|check':'').')$~',$Vc);}function +kill_process($u){return +queries("KILL ".number($u));}function +connection_id(){return"SELECT CONNECTION_ID()";}function +max_connections(){return +get_val("SELECT @@max_connections");}function +types(){return +array();}function +type_values($u){return"";}function +schemas(){return +array();}function +get_schema(){return"";}function +set_schema($Jh,$h=null){return +true;}}define('Adminer\JUSH',Driver::$jush);define('Adminer\SERVER',"".$_GET[DRIVER]);define('Adminer\DB',"$_GET[db]");define('Adminer\ME',preg_replace('~\?.*~','',relative_uri()).'?'.(sid()?SID.'&':'').(SERVER!==null?DRIVER."=".urlencode(SERVER).'&':'').($_GET["ext"]?"ext=".urlencode($_GET["ext"]).'&':'').(isset($_GET["username"])?"username=".urlencode($_GET["username"]).'&':'').(DB!=""?'db='.urlencode(DB).'&'.(isset($_GET["ns"])?"ns=".urlencode($_GET["ns"])."&":""):''));function +page_header($Wi,$m="",$Oa=array(),$Xi=""){page_headers();if(is_ajax()&&$m){page_messages($m);exit;}if(!ob_get_level())ob_start('ob_gzhandler',4096);$Yi=$Wi.($Xi!=""?": $Xi":"");$Zi=strip_tags($Yi.(SERVER!=""&&SERVER!="localhost"?h(" - ".SERVER):"")." - ".adminer()->name());echo' + + + + +',$Zi,' + +';$Jb=adminer()->css();if(is_int(key($Jb)))$Jb=array_fill_keys($Jb,'light');$Fd=in_array('light',$Jb)||in_array('',$Jb);$Dd=in_array('dark',$Jb)||in_array('',$Jb);$Mb=($Fd?($Dd?null:false):($Dd?:null));$nf=" media='(prefers-color-scheme: dark)'";if($Mb!==false)echo"\n";echo"\n",script_src(preg_replace("~\\?.*~","",ME)."?file=functions.js&version=5.4.2");if(adminer()->head($Mb))echo"\n","\n";foreach($Jb +as$Dj=>$yf){$_a=($yf=='dark'&&!$Mb?$nf:($yf=='light'&&$Dd?" media='(prefers-color-scheme: light)'":""));echo"\n";}echo"\n\n";$p=get_temp_dir()."/adminer.version";echo +script("mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick".(isset($_COOKIE["adminer_version"])?"":", onload: partial(verifyVersion, '".VERSION."')")."}); +document.body.classList.replace('nojs', 'js'); +const offlineMessage = '".js_escape(lang(87))."'; +const thousandsSeparator = '".js_escape(lang(4))."';"),"\n",script("mixin(qs('#help'), {onmouseover: () => { helpOpen = 1; }, onmouseout: helpMouseout});"),"
    \n","".icon("move","","menu","")."".script("qs('#menuopen').onclick = event => { qs('#foot').classList.toggle('foot'); event.stopPropagation(); }");if($Oa!==null){$_=substr(preg_replace('~\b(username|db|ns)=[^&]*&~','',ME),0,-1);echo'

    $Yi

    \n","\n";restart_session();page_messages($m);$j=&get_session("dbs");if(DB!=""&&$j&&!in_array(DB,$j,true))$j=null;stop_session();define('Adminer\PAGE_HEADER',1);}function +page_headers(){header("Content-Type: text/html; charset=utf-8");header("Cache-Control: no-cache");header("X-Frame-Options: deny");header("X-XSS-Protection: 0");header("X-Content-Type-Options: nosniff");header("Referrer-Policy: origin-when-cross-origin");foreach(adminer()->csp(csp())as$Ib){$Hd=array();foreach($Ib +as$y=>$X)$Hd[]="$y $X";header("Content-Security-Policy: ".implode("; ",$Hd));}adminer()->headers();}function +csp(){return +array(array("script-src"=>"'self' 'unsafe-inline' 'nonce-".get_nonce()."' 'strict-dynamic'","connect-src"=>"'self' https://www.adminer.org","frame-src"=>"'none'","object-src"=>"'none'","base-uri"=>"'none'","form-action"=>"'self'",),);}function +get_nonce(){static$Kf;if(!$Kf)$Kf=base64_encode(rand_string());return$Kf;}function +page_messages($m){$Cj=preg_replace('~^[^?]*~','',$_SERVER["REQUEST_URI"]);$uf=idx($_SESSION["messages"],$Cj);if($uf){echo"
    ".implode("
    \n
    ",$uf)."
    ".script("messagesPrint();");unset($_SESSION["messages"][$Cj]);}if($m)echo"
    $m
    \n";if(adminer()->error)echo"
    ".adminer()->error."
    \n";}function +page_footer($xf=""){echo"
    \n\n\n\n",script("setupSubmitHighlight(document);");}function +int32($Cf){while($Cf>=2147483648)$Cf-=4294967296;while($Cf<=-2147483649)$Cf+=4294967296;return(int)$Cf;}function +long2str(array$W,$Uj){$Hh='';foreach($W +as$X)$Hh +.=pack('V',$X);if($Uj)return +substr($Hh,0,end($W));return$Hh;}function +str2long($Hh,$Uj){$W=array_values(unpack('V*',str_pad($Hh,4*ceil(strlen($Hh)/4),"\0")));if($Uj)$W[]=strlen($Hh);return$W;}function +xxtea_mx($bk,$ak,$zi,$Ce){return +int32((($bk>>5&0x7FFFFFF)^$ak<<2)+(($ak>>3&0x1FFFFFFF)^$bk<<4))^int32(($zi^$ak)+($Ce^$bk));}function +encrypt_string($ui,$y){if($ui=="")return"";$y=array_values(unpack("V*",pack("H*",md5($y))));$W=str2long($ui,true);$Cf=count($W)-1;$bk=$W[$Cf];$ak=$W[0];$hh=floor(6+52/($Cf+1));$zi=0;while($hh-->0){$zi=int32($zi+0x9E3779B9);$pc=$zi>>2&3;for($xg=0;$xg<$Cf;$xg++){$ak=$W[$xg+1];$Bf=xxtea_mx($bk,$ak,$zi,$y[$xg&3^$pc]);$bk=int32($W[$xg]+$Bf);$W[$xg]=$bk;}$ak=$W[0];$Bf=xxtea_mx($bk,$ak,$zi,$y[$xg&3^$pc]);$bk=int32($W[$Cf]+$Bf);$W[$Cf]=$bk;}return +long2str($W,false);}function +decrypt_string($ui,$y){if($ui=="")return"";if(!$y)return +false;$y=array_values(unpack("V*",pack("H*",md5($y))));$W=str2long($ui,false);$Cf=count($W)-1;$bk=$W[$Cf];$ak=$W[0];$hh=floor(6+52/($Cf+1));$zi=int32($hh*0x9E3779B9);while($zi){$pc=$zi>>2&3;for($xg=$Cf;$xg>0;$xg--){$bk=$W[$xg-1];$Bf=xxtea_mx($bk,$ak,$zi,$y[$xg&3^$pc]);$ak=int32($W[$xg]-$Bf);$W[$xg]=$ak;}$bk=$W[$Cf];$Bf=xxtea_mx($bk,$ak,$zi,$y[$xg&3^$pc]);$ak=int32($W[0]-$Bf);$W[0]=$ak;$zi=int32($zi-0x9E3779B9);}return +long2str($W,true);}$Ng=array();if($_COOKIE["adminer_permanent"]){foreach(explode(" ",$_COOKIE["adminer_permanent"])as$X){list($y)=explode(":",$X);$Ng[$y]=$X;}}function +add_invalid_login(){$Ha=get_temp_dir()."/adminer.invalid";foreach(glob("$Ha*")?:array($Ha)as$p){$r=file_open_lock($p);if($r)break;}if(!$r)$r=file_open_lock("$Ha-".rand_string());if(!$r)return;$te=unserialize(stream_get_contents($r));$Ti=time();if($te){foreach($te +as$ue=>$X){if($X[0]<$Ti)unset($te[$ue]);}}$se=&$te[adminer()->bruteForceKey()];if(!$se)$se=array($Ti+30*60,0);$se[1]++;file_write_unlock($r,serialize($te));}function +check_invalid_login(array&$Ng){$te=array();foreach(glob(get_temp_dir()."/adminer.invalid*")as$p){$r=file_open_lock($p);if($r){$te=unserialize(stream_get_contents($r));file_unlock($r);break;}}$se=idx($te,adminer()->bruteForceKey(),array());$Jf=($se[1]>29?$se[0]-time():0);if($Jf>0)auth_error(lang(89,ceil($Jf/60)),$Ng);}$Aa=$_POST["auth"];if($Aa){session_regenerate_id();$Pj=$Aa["driver"];$N=$Aa["server"];$V=$Aa["username"];$F=(string)$Aa["password"];$k=$Aa["db"];set_password($Pj,$N,$V,$F);$_SESSION["db"][$Pj][$N][$V][$k]=true;if($Aa["permanent"]){$y=implode("-",array_map('base64_encode',array($Pj,$N,$V,$k)));$ch=adminer()->permanentLogin(true);$Ng[$y]="$y:".base64_encode($ch?encrypt_string($F,$ch):"");cookie("adminer_permanent",implode(" ",$Ng));}if(count($_POST)==1||DRIVER!=$Pj||SERVER!=$N||$_GET["username"]!==$V||DB!=$k)redirect(auth_url($Pj,$N,$V,$k));}elseif($_POST["logout"]&&(!$_SESSION["token"]||verify_token())){foreach(array("pwds","db","dbs","queries")as$y)set_session($y,null);unset_permanent($Ng);redirect(substr(preg_replace('~\b(username|db|ns)=[^&]*&~','',ME),0,-1),lang(90).' '.lang(91));}elseif($Ng&&!$_SESSION["pwds"]){session_regenerate_id();$ch=adminer()->permanentLogin();foreach($Ng +as$y=>$X){list(,$eb)=explode(":",$X);list($Pj,$N,$V,$k)=array_map('base64_decode',explode("-",$y));set_password($Pj,$N,$V,decrypt_string(base64_decode($eb),$ch));$_SESSION["db"][$Pj][$N][$V][$k]=true;}}function +unset_permanent(array&$Ng){foreach($Ng +as$y=>$X){list($Pj,$N,$V,$k)=array_map('base64_decode',explode("-",$y));if($Pj==DRIVER&&$N==SERVER&&$V==$_GET["username"]&&$k==DB)unset($Ng[$y]);}cookie("adminer_permanent",implode(" ",$Ng));}function +auth_error($m,array&$Ng){$bi=session_name();if(isset($_GET["username"])){header("HTTP/1.1 403 Forbidden");if(($_COOKIE[$bi]||$_GET[$bi])&&!$_SESSION["token"])$m=lang(92);else{restart_session();add_invalid_login();$F=get_password();if($F!==null){if($F===false)$m +.=($m?'
    ':'').lang(93,target_blank(),'permanentLogin()');set_password(DRIVER,SERVER,$_GET["username"],null);}unset_permanent($Ng);}}if(!$_COOKIE[$bi]&&$_GET[$bi]&&ini_bool("session.use_only_cookies"))$m=lang(94);$_g=session_get_cookie_params();cookie("adminer_key",($_COOKIE["adminer_key"]?:rand_string()),$_g["lifetime"]);if(!$_SESSION["token"])$_SESSION["token"]=rand(1,1e6);page_header(lang(39),$m,null);echo"
    \n","
    ";if(hidden_fields($_POST,array("auth")))echo"

    ".lang(95)."\n";echo"

    \n";adminer()->loginForm();echo"
    \n";page_footer("auth");exit;}if(isset($_GET["username"])&&!class_exists('Adminer\Db')){unset($_SESSION["pwds"][DRIVER]);unset_permanent($Ng);page_header(lang(96),lang(97,implode(", ",Driver::$extensions)),false);page_footer("auth");exit;}$g='';if(isset($_GET["username"])&&is_string(get_password())){list(,$Rg)=host_port(SERVER);if(preg_match('~^\s*([-+]?\d+)~',$Rg,$A)&&($A[1]<1024||$A[1]>65535))auth_error(lang(98),$Ng);check_invalid_login($Ng);$Hb=adminer()->credentials();$g=Driver::connect($Hb[0],$Hb[1],$Hb[2]);if(is_object($g)){Db::$instance=$g;Driver::$instance=new +Driver($g);if($g->flavor)save_settings(array("vendor-".DRIVER."-".SERVER=>get_driver(DRIVER)));}}$Xe=null;if(!is_object($g)||($Xe=adminer()->login($_GET["username"],get_password()))!==true){$m=(is_string($g)?nl_br(h($g)):(is_string($Xe)?$Xe:lang(99))).(preg_match('~^ | $~',get_password())?'
    '.lang(100):'');auth_error($m,$Ng);}if($_POST["logout"]&&$_SESSION["token"]&&!verify_token()){page_header(lang(88),lang(101));page_footer("db");exit;}if(!$_SESSION["token"])$_SESSION["token"]=rand(1,1e6);stop_session(true);if($Aa&&$_POST["token"])$_POST["token"]=get_token();$m='';if($_POST){if(!verify_token()){$le="max_input_vars";$lf=ini_get($le);if(extension_loaded("suhosin")){foreach(array("suhosin.request.max_vars","suhosin.post.max_vars")as$y){$X=ini_get($y);if($X&&(!$lf||$X<$lf)){$le=$y;$lf=$X;}}}$m=(!$_POST["token"]&&$lf?lang(102,"'$le'"):lang(101).' '.lang(103));}}elseif($_SERVER["REQUEST_METHOD"]=="POST"){$m=lang(104,"'post_max_size'");if(isset($_GET["sql"]))$m +.=' '.lang(105);}function +print_select_result($I,$h=null,array$mg=array(),$z=0){$Ve=array();$x=array();$e=array();$Ma=array();$rj=array();$J=array();for($t=0;(!$z||$t<$z)&&($K=$I->fetch_row());$t++){if(!$t){echo"
    \n","\n","";for($_e=0;$_efetch_field();$B=$n->name;$lg=(isset($n->orgtable)?$n->orgtable:"");$kg=(isset($n->orgname)?$n->orgname:$B);if($mg&&JUSH=="sql")$Ve[$_e]=($B=="table"?"table=":($B=="possible_keys"?"indexes=":null));elseif($lg!=""){if(isset($n->table))$J[$n->table]=$lg;if(!isset($x[$lg])){$x[$lg]=array();foreach(indexes($lg,$h)as$w){if($w["type"]=="PRIMARY"){$x[$lg]=array_flip($w["columns"]);break;}}$e[$lg]=$x[$lg];}if(isset($e[$lg][$kg])){unset($e[$lg][$kg]);$x[$lg][$kg]=$_e;$Ve[$_e]=$lg;}}if($n->charsetnr==63)$Ma[$_e]=true;$rj[$_e]=$n->type;echo"name!=$kg?" title='".h(($lg!=""?"$lg.":"").$kg)."'":"").">".h($B).($mg?doc_link(array('sql'=>"explain-output.html#explain_".strtolower($B),'mariadb'=>"explain/#the-columns-in-explain-select",)):"");}echo"\n";}echo"";foreach($K +as$y=>$X){$_="";if(isset($Ve[$y])&&!$e[$Ve[$y]]){if($mg&&JUSH=="sql"){$R=$K[array_search("table=",$Ve)];$_=ME.$Ve[$y].urlencode($mg[$R]!=""?$mg[$R]:$R);}else{$_=ME."edit=".urlencode($Ve[$y]);foreach($x[$Ve[$y]]as$jb=>$_e){if($K[$_e]===null){$_="";break;}$_ +.="&where".urlencode("[".bracket_escape($jb)."]")."=".urlencode($K[$_e]);}}}$n=array('type'=>($Ma[$y]?'blob':($rj[$y]==254?'char':'')),);$X=select_value($X,$_,$n,null);echo"$X";}}echo($t?"
    \n
    ":"

    ".lang(14))."\n";return$J;}function +referencable_primary($Th){$J=array();foreach(table_status('',true)as$Di=>$R){if($Di!=$Th&&fk_support($R)){foreach(fields($Di)as$n){if($n["primary"]){if($J[$Di]){unset($J[$Di]);break;}$J[$Di]=$n;}}}}return$J;}function +textarea($B,$Y,$L=10,$mb=80){echo"";}function +select_input($_a,array$gg,$Y="",$ag="",$Og=""){$Li=($gg?"select":"input");return"<$Li$_a".($gg?">

    ".lang(125,get_driver(DRIVER),"".h(connection()->server_info)."","".connection()->extension."")."\n","

    ".lang(126,"".h(logged_user())."")."\n";$j=adminer()->databases();if($j){$Lh=support("scheme");$lb=collations();echo"

    \n","\n",script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});"),"".(support("database")?"\n";$j=($_GET["dbsize"]?count_tables($j):array_flip($j));foreach($j +as$k=>$T){$Ch=h(ME)."db=".urlencode($k);$u=h("Db-".$k);echo"".(support("database")?"
    ":"")."".lang(38).(get_session("dbs")!==null?" - ".lang(127)."":"")."".lang(128)."".lang(129)."".lang(130)." - ".lang(131)."".script("qsl('a').onclick = partial(ajaxSetHtml, '".js_escape(ME)."script=connect');","")."
    ".checkbox("db[]",$k,in_array($k,(array)$_POST["db"]),"","","",$u):""),"".h($k)."";$c=h(db_collation($k,$lb));echo"".(support("database")?"$c":$c),"".($_GET["dbsize"]?$T:"?")."","".($_GET["dbsize"]?db_size($k):"?"),"\n";}echo"
    \n",(support("database")?"\n":""),input_token(),"
    \n",script("tableCheck();");}if(!empty(adminer()->plugins)){echo"
    \n","

    ".lang(134)."

    \n
      \n";foreach(adminer()->plugins +as$Pg){$bc=(method_exists($Pg,'description')?$Pg->description():"");if(!$bc){$sh=new +\ReflectionObject($Pg);if(preg_match('~^/[\s*]+(.+)~',$sh->getDocComment(),$A))$bc=$A[1];}$Mh=(method_exists($Pg,'screenshot')?$Pg->screenshot():"");echo"
    • ".get_class($Pg)."".h($bc?": $bc":"").($Mh?" (".lang(135).")":"")."\n";}echo"
    \n";adminer()->pluginsLinks();echo"
    \n";}}page_footer("db");exit;}if(support("scheme")){if(DB!=""&&$_GET["ns"]!==""){if(!isset($_GET["ns"]))redirect(preg_replace('~ns=[^&]*&~','',ME)."ns=".get_schema());if(!set_schema($_GET["ns"])){header("HTTP/1.1 404 Not Found");page_header(lang(80).": ".h($_GET["ns"]),lang(136),true);page_footer("ns");exit;}}}adminer()->afterConnect();class +TmpFile{private$handler;var$size;function +__construct(){$this->handler=tmpfile();}function +write($Ab){$this->size+=strlen($Ab);fwrite($this->handler,$Ab);}function +send(){fseek($this->handler,0);fpassthru($this->handler);fclose($this->handler);}}if(isset($_GET["select"])&&($_POST["edit"]||$_POST["clone"])&&!$_POST["save"])$_GET["edit"]=$_GET["select"];if(isset($_GET["callf"]))$_GET["call"]=$_GET["callf"];if(isset($_GET["function"]))$_GET["procedure"]=$_GET["function"];if(isset($_GET["download"])){$a=$_GET["download"];$o=fields($a);header("Content-Type: application/octet-stream");header("Content-Disposition: attachment; filename=".friendly_url("$a-".implode("_",$_GET["where"])).".".friendly_url($_GET["field"]));$M=array(idf_escape($_GET["field"]));$I=driver()->select($a,$M,array(where($_GET,$o)),$M);$K=($I?$I->fetch_row():array());echo +driver()->value($K[0],$o[$_GET["field"]]);exit;}elseif(isset($_GET["table"])){$a=$_GET["table"];$o=fields($a);if(!$o)$m=error()?:lang(11);$S=table_status1($a);$B=adminer()->tableName($S);page_header(($o&&is_view($S)?$S['Engine']=='materialized view'?lang(137):lang(138):lang(139)).": ".($B!=""?$B:h($a)),$m);$Bh=array();foreach($o +as$y=>$n)$Bh+=$n["privileges"];adminer()->selectLinks($S,(isset($Bh["insert"])||!support("table")?"":null));$qb=$S["Comment"];if($qb!="")echo"

    ".lang(51).": ".h($qb)."\n";if($o)adminer()->tableStructurePrint($o,$S);function +tables_links(array$T){echo"

    \n";}$ke=driver()->inheritsFrom($a);if($ke){echo"

    ".lang(140)."

    \n";tables_links($ke);}if(support("indexes")&&driver()->supportsIndex($S)){echo"

    ".lang(141)."

    \n";$x=indexes($a);if($x)adminer()->tableIndexesPrint($x,$S);echo'

    ".lang(106)."

    \n";$md=foreign_keys($a);if($md){echo"\n","\n";foreach($md +as$B=>$q){echo"","
    ".lang(143)."".lang(144)."".lang(109)."".lang(108)."
    ".implode(", ",array_map('Adminer\h',$q["source"]))."";$_=($q["db"]!=""?preg_replace('~db=[^&]*~',"db=".urlencode($q["db"]),ME):($q["ns"]!=""?preg_replace('~ns=[^&]*~',"ns=".urlencode($q["ns"]),ME):ME));echo"".($q["db"]!=""&&$q["db"]!=DB?"".h($q["db"]).".":"").($q["ns"]!=""&&$q["ns"]!=$_GET["ns"]?"".h($q["ns"]).".":"").h($q["table"])."","(".implode(", ",array_map('Adminer\h',$q["target"])).")","".h($q["on_delete"]),"".h($q["on_update"]),''.lang(145).'',"\n";}echo"
    \n";}echo'

    ".lang(147)."

    \n";$Za=driver()->checkConstraints($a);if($Za){echo"\n";foreach($Za +as$y=>$X)echo"","
    ".h($X),"".lang(145)."","\n";echo"
    \n";}echo'

    ".lang(149)."

    \n";$oj=triggers($a);if($oj){echo"\n";foreach($oj +as$y=>$X)echo"
    ".h($X[0])."".h($X[1])."".h($y)."".lang(145)."\n";echo"
    \n";}echo'

    ".lang(151)."

    \n";$Dg=driver()->partitionsInfo($a);if($Dg)echo"

    BY ".h("$Dg[partition_by]($Dg[partition])")."\n";tables_links($je);}}elseif(isset($_GET["schema"])){page_header(lang(71),"",array(),h(DB.($_GET["ns"]?".$_GET[ns]":"")));$Fi=array();$Gi=array();$da=($_GET["schema"]?:$_COOKIE["adminer_schema-".str_replace(".","_",DB)]);preg_match_all('~([^:]+):([-0-9.]+)x([-0-9.]+)(_|$)~',$da,$df,PREG_SET_ORDER);foreach($df +as$t=>$A){$Fi[$A[1]]=array($A[2],$A[3]);$Gi[]="\n\t'".js_escape($A[1])."': [ $A[2], $A[3] ]";}$dj=0;$Ia=-1;$Jh=array();$rh=array();$Pe=array();$ua=driver()->allFields();foreach(table_status('',true)as$R=>$S){if(is_view($S))continue;$Sg=0;$Jh[$R]["fields"]=array();foreach($ua[$R]as$n){$Sg+=1.25;$n["pos"]=$Sg;$Jh[$R]["fields"][$n["field"]]=$n;}$Jh[$R]["pos"]=($Fi[$R]?:array($dj,0));foreach(adminer()->foreignKeys($R)as$X){if(!$X["db"]){$Ne=$Ia;if(idx($Fi[$R],1)||idx($Fi[$X["table"]],1))$Ne=min(idx($Fi[$R],1,0),idx($Fi[$X["table"]],1,0))-1;else$Ia-=.1;while($Pe[(string)$Ne])$Ne-=.0001;$Jh[$R]["references"][$X["table"]][(string)$Ne]=array($X["source"],$X["target"]);$rh[$X["table"]][$R][(string)$Ne]=$X["target"];$Pe[(string)$Ne]=true;}}$dj=max($dj,$Jh[$R]["pos"][0]+2.5+$Sg);}echo'

    + +qs(\'#schema\').onselectstart = () => false; +const tablePos = {',implode(",",$Gi)."\n",'}; +const em = qs(\'#schema\').offsetHeight / ',$dj,'; +document.onmousemove = schemaMousemove; +document.onmouseup = partialArg(schemaMouseup, \'',js_escape(DB),'\'); + +';foreach($Jh +as$B=>$R){echo"
    ",''.h($B)."",script("qsl('div').onmousedown = schemaMousedown;");foreach($R["fields"]as$n){$X=''.h($n["field"]).'';echo"
    ".($n["primary"]?"$X":$X);}foreach((array)$R["references"]as$Ni=>$th){foreach($th +as$Ne=>$oh){$Oe=$Ne-idx($Fi[$B],1);$t=0;foreach($oh[0]as$ji)echo"\n
    "."
    ";}}foreach((array)$rh[$B]as$Ni=>$th){foreach($th +as$Ne=>$e){$Oe=$Ne-idx($Fi[$B],1);$t=0;foreach($e +as$Mi)echo"\n
    "."
    "."
    ";}}echo"\n
    \n";}foreach($Jh +as$B=>$R){foreach((array)$R["references"]as$Ni=>$th){foreach($th +as$Ne=>$oh){$wf=$dj;$jf=-10;foreach($oh[0]as$y=>$ji){$Tg=$R["pos"][0]+$R["fields"][$ji]["pos"];$Ug=$Jh[$Ni]["pos"][0]+$Jh[$Ni]["fields"][$oh[1][$y]]["pos"];$wf=min($wf,$Tg,$Ug);$jf=max($jf,$Tg,$Ug);}echo"
    \n";}}}echo'
    +
    + +';$Rb=array('','USE','DROP+CREATE','CREATE');$Hi=array('','DROP+CREATE','CREATE');$Ob=array('','TRUNCATE+INSERT','INSERT');if(JUSH=="sql")$Ob[]='INSERT+UPDATE';$K=get_settings("adminer_export");if(!$K)$K=array("output"=>"text","format"=>"sql","db_style"=>(DB!=""?"":"CREATE"),"table_style"=>"DROP+CREATE","data_style"=>"INSERT");if(!isset($K["events"])){$K["routines"]=$K["events"]=($_GET["dump"]=="");$K["triggers"]=$K["table_style"];}echo"
    ".lang(153)."".html_radios("output",adminer()->dumpOutput(),$K["output"])."\n","
    ".lang(154)."".html_radios("format",adminer()->dumpFormat(),$K["format"])."\n",(JUSH=="sqlite"?"":"
    ".lang(38)."".html_select('db_style',$Rb,$K["db_style"]).(support("type")?checkbox("types",1,$K["types"],lang(6)):"").(support("routine")?checkbox("routines",1,$K["routines"],lang(73)):"").(support("event")?checkbox("events",1,$K["events"],lang(75)):"")),"
    ".lang(129)."".html_select('table_style',$Hi,$K["table_style"]).checkbox("auto_increment",1,$K["auto_increment"],lang(52)).(support("trigger")?checkbox("triggers",1,$K["triggers"],lang(149)):""),"
    ".lang(155)."".html_select('data_style',$Ob,$K["data_style"]),'
    +

    +',input_token(),' + +',script("qsl('table').onclick = dumpClick;");$Yg=array();if(DB!=""){$bb=($a!=""?"":" checked");echo"","\n";$Sj="";$Ji=tables_list();foreach($Ji +as$B=>$U){$Xg=preg_replace('~_.*~','',$B);$bb=($a==""||$a==(substr($a,-1)=="%"?"$Xg%":$B));$bh="\n";$j=adminer()->databases();if($j){foreach($j +as$k){if(!information_schema($k)){$Xg=preg_replace('~_.*~','',$k);echo"
    ".script("qs('#check-tables').onclick = partial(formCheck, /^tables\\[/);",""),"".script("qs('#check-data').onclick = partial(formCheck, /^data\\[/);",""),"
    ".checkbox("tables[]",$B,$bb,$B,"","block");if($U!==null&&!preg_match('~table~i',$U))$Sj +.="$bh\n";else +echo"$bh\n";$Yg[$Xg]++;}echo$Sj;if($Ji)echo +script("ajaxSetHtml('".js_escape(ME)."script=db');");}else{echo"
    ","",script("qs('#check-databases').onclick = partial(formCheck, /^databases\\[/);",""),"
    ".checkbox("databases[]",$k,$a==""||$a=="$Xg%",$k,"","block")."\n";$Yg[$Xg]++;}}}else +echo"
    ";}echo'
    +

    +';$cd=true;foreach($Yg +as$y=>$X){if($y!=""&&$X>1){echo($cd?"

    ":" ")."".h($y)."";$cd=false;}}}elseif(isset($_GET["privileges"])){page_header(lang(72));echo'

    \n";hidden_fields_get();echo +input_hidden("db",DB),($vd?"":input_hidden("grant")),"\n","\n";while($K=$I->fetch_assoc())echo'
    ".lang(36)."".lang(34)."
    '.h($K["User"])."".h($K["Host"]).''.lang(12)."\n";if(!$vd||DB!="")echo"
    \n";echo"
    \n","

    \n";}elseif(isset($_GET["sql"])){if(!$m&&$_POST["export"]){save_settings(array("output"=>$_POST["output"],"format"=>$_POST["format"]),"adminer_import");dump_headers("sql");if($_POST["format"]=="sql")echo"$_POST[query]\n";else{adminer()->dumpTable("","");adminer()->dumpData("","table",$_POST["query"]);adminer()->dumpFooter();}exit;}restart_session();$Ld=&get_session("queries");$Kd=&$Ld[DB];if(!$m&&$_POST["clear"]){$Kd=array();redirect(remove_from_uri("history"));}stop_session();page_header((isset($_GET["import"])?lang(76):lang(65)),$m);$Ue='--'.(JUSH=='sql'?' ':'');if(!$m&&$_POST){$r=false;if(!isset($_GET["import"]))$H=$_POST["query"];elseif($_POST["webfile"]){$ni=adminer()->importServerPath();$r=@fopen((file_exists($ni)?$ni:"compress.zlib://$ni.gz"),"rb");$H=($r?fread($r,1e6):false);}else$H=get_file("sql_file",true,";");if(is_string($H)){if(function_exists('memory_get_usage')&&($of=ini_bytes("memory_limit"))!="-1")@ini_set("memory_limit",max($of,strval(2*strlen($H)+memory_get_usage()+8e6)));if($H!=""&&strlen($H)<1e6){$hh=$H.(preg_match("~;[ \t\r\n]*\$~",$H)?"":";");if(!$Kd||first(end($Kd))!=$hh){restart_session();$Kd[]=array($hh,time());set_session("queries",$Ld);stop_session();}}$ki="(?:\\s|/\\*[\s\S]*?\\*/|(?:#|$Ue)[^\n]*\n?|--\r?\n)";$Zb=driver()->delimiter;$C=0;$xc=true;$h=connect();if($h&&DB!=""){$h->select_db(DB);if($_GET["ns"]!="")set_schema($_GET["ns"],$h);}$pb=0;$Ec=array();$Ag='[\'"'.(JUSH=="sql"?'`#':(JUSH=="sqlite"?'`[':(JUSH=="mssql"?'[':''))).']|/\*|'.$Ue.'|$'.(JUSH=="pgsql"?'|\$([a-zA-Z]\w*)?\$':'');$ej=microtime(true);$oa=get_settings("adminer_import");while($H!=""){if(!$C&&preg_match("~^$ki*+DELIMITER\\s+(\\S+)~i",$H,$A)){$Zb=preg_quote($A[1]);$H=substr($H,strlen($A[0]));}elseif(!$C&&JUSH=='pgsql'&&preg_match("~^($ki*+COPY\\s+)[^;]+\\s+FROM\\s+stdin;~i",$H,$A)){$Zb="\n\\\\\\.\r?\n";$C=strlen($A[0]);}else{preg_match("($Zb\\s*|$Ag)",$H,$A,PREG_OFFSET_CAPTURE,$C);list($od,$Sg)=$A[0];if(!$od&&$r&&!feof($r))$H +.=fread($r,1e5);else{if(!$od&&rtrim($H)=="")break;$C=$Sg+strlen($od);if($od&&!preg_match("(^$Zb)",$od)){$Ta=driver()->hasCStyleEscapes()||(JUSH=="pgsql"&&($Sg>0&&strtolower($H[$Sg-1])=="e"));$Lg=($od=='/*'?'\*/':($od=='['?']':(preg_match("~^$Ue|^#~",$od)?"\n":preg_quote($od).($Ta?'|\\\\.':''))));while(preg_match("($Lg|\$)s",$H,$A,PREG_OFFSET_CAPTURE,$C)){$Hh=$A[0][0];if(!$Hh&&$r&&!feof($r))$H +.=fread($r,1e5);else{$C=$A[0][1]+strlen($Hh);if(!$Hh||$Hh[0]!="\\")break;}}}else{$xc=false;$hh=substr($H,0,$Sg+($Zb[0]=="\n"?3:0));$pb++;$bh="
    ".adminer()->sqlCommandQuery($hh)."
    \n";if(JUSH=="sqlite"&&preg_match("~^$ki*+ATTACH\\b~i",$hh,$A)){echo$bh,"

    ".lang(157)."\n";$Ec[]=" $pb";if($_POST["error_stops"])break;}else{if(!$_POST["only_errors"]){echo$bh;ob_flush();flush();}$si=microtime(true);if(connection()->multi_query($hh)&&$h&&preg_match("~^$ki*+USE\\b~i",$hh))$h->query($hh);do{$I=connection()->store_result();if(connection()->error){echo($_POST["only_errors"]?$bh:""),"

    ".lang(158).(connection()->errno?" (".connection()->errno.")":"").": ".error()."\n";$Ec[]=" $pb";if($_POST["error_stops"])break +2;}else{$Ti=" (".format_time($si).")".(strlen($hh)<1000?" ".lang(12)."":"");$qa=connection()->affected_rows;$Vj=($_POST["only_errors"]?"":driver()->warnings());$Wj="warnings-$pb";if($Vj)$Ti +.=", ".lang(47)."".script("qsl('a').onclick = partial(toggle, '$Wj');","");$Mc=null;$mg=null;$Nc="explain-$pb";if(is_object($I)){$z=$_POST["limit"];$mg=print_select_result($I,$h,array(),$z);if(!$_POST["only_errors"]){echo"

    \n";$Nf=$I->num_rows;echo"
    \n";}}else{if(preg_match("~^$ki*+(CREATE|DROP|ALTER)$ki++(DATABASE|SCHEMA)\\b~i",$hh)){restart_session();set_session("dbs",null);stop_session();}if(!$_POST["only_errors"])echo"

    ".lang(161,$qa)."$Ti\n";}echo($Vj?"

    \n":"");if($Mc){echo"\n";}}$si=microtime(true);}while(connection()->next_result());}$H=substr($H,$C);$C=0;}}}}if($xc)echo"

    ".lang(162)."\n";elseif($_POST["only_errors"])echo"

    ".lang(163,$pb-count($Ec))," (".format_time($ej).")\n";elseif($Ec&&$pb>1)echo"

    ".lang(158).": ".implode("",$Ec)."\n";}else +echo"

    ".upload_error($H)."\n";}echo' +

    +';$Kc="";if(!isset($_GET["import"])){$hh=$_GET["sql"];if($_POST)$hh=$_POST["query"];elseif($_GET["history"]=="all")$hh=$Kd;elseif($_GET["history"]!="")$hh=idx($Kd[$_GET["history"]],0);echo"

    ";textarea("query",$hh,20);echo +script(($_POST?"":"qs('textarea').focus();\n")."qs('#form').onsubmit = partial(sqlSubmit, qs('#form'), '".js_escape(remove_from_uri("sql|limit|error_stops|only_errors|history"))."');"),"

    ";adminer()->sqlPrintAfter();echo"$Kc\n",lang(165).": \n";}else{$Ad=(extension_loaded("zlib")?"[.gz]":"");echo"

    ".lang(166)."
    ",file_input("SQL$Ad: \n$Kc"),"
    \n";$Wd=adminer()->importServerPath();if($Wd)echo"
    ".lang(167)."
    ",lang(168,"".h($Wd)."$Ad"),' ',"
    \n";echo"

    ";}echo +checkbox("error_stops",1,($_POST?$_POST["error_stops"]:isset($_GET["import"])||$_GET["error_stops"]),lang(170))."\n",checkbox("only_errors",1,($_POST?$_POST["only_errors"]:isset($_GET["import"])||$_GET["only_errors"]),lang(171))."\n",input_token();if(!isset($_GET["import"])&&$Kd){print_fieldset("history",lang(172),$_GET["history"]!="");for($X=end($Kd);$X;$X=prev($Kd)){$y=key($Kd);list($hh,$Ti,$sc)=$X;echo''.lang(12).""." ".@date("H:i:s",$Ti).""." ".shorten_utf8(ltrim(str_replace("\n"," ",str_replace("\r","",preg_replace("~^(#|$Ue).*~m",'',$hh)))),80,"").($sc?" ($sc)":"")."
    \n";}echo"\n","".lang(174)."\n","\n";}echo'

    +';}elseif(isset($_GET["edit"])){$a=$_GET["edit"];$o=fields($a);$Z=(isset($_GET["select"])?($_POST["check"]&&count($_POST["check"])==1?where_check($_POST["check"][0],$o):""):where($_GET,$o));$_j=(isset($_GET["select"])?$_POST["edit"]:$Z);foreach($o +as$B=>$n){if((!$_j&&!isset($n["privileges"]["insert"]))||adminer()->fieldName($n)=="")unset($o[$B]);}if($_POST&&!$m&&!isset($_GET["select"])){$We=$_POST["referer"];if($_POST["insert"])$We=($_j?null:$_SERVER["REQUEST_URI"]);elseif(!preg_match('~^.+&select=.+$~',$We))$We=ME."select=".urlencode($a);$x=indexes($a);$vj=unique_array($_GET["where"],$x);$kh="\nWHERE $Z";if(isset($_POST["delete"]))queries_redirect($We,lang(175),driver()->delete($a,$kh,$vj?0:1));else{$O=array();foreach($o +as$B=>$n){$X=process_input($n);if($X!==false&&$X!==null)$O[idf_escape($B)]=$X;}if($_j){if(!$O)redirect($We);queries_redirect($We,lang(176),driver()->update($a,$O,$kh,$vj?0:1));if(is_ajax()){page_headers();page_messages($m);exit;}}else{$I=driver()->insert($a,$O);$Me=($I?last_id($I):0);queries_redirect($We,lang(177,($Me?" $Me":"")),$I);}}}$K=null;if($Z){$M=array();foreach($o +as$B=>$n){if(isset($n["privileges"]["select"])){$ya=($_POST["clone"]&&$n["auto_increment"]?"''":convert_field($n));$M[]=($ya?"$ya AS ":"").idf_escape($B);}}$K=array();if(!support("table"))$M=array("*");if($M){$I=driver()->select($a,$M,array($Z),$M,array(),(isset($_GET["select"])?2:1));if(!$I)$m=error();else{$K=$I->fetch_assoc();if(!$K)$K=false;}if(isset($_GET["select"])&&(!$K||$I->fetch_assoc()))$K=null;}}if(!support("table")&&!$o){if(!$Z){$I=driver()->select($a,array("*"),array(),array("*"));$K=($I?$I->fetch_assoc():false);if(!$K)$K=array(driver()->primary=>"");}if($K){foreach($K +as$y=>$X){if(!$Z)$K[$y]=null;$o[$y]=array("field"=>$y,"null"=>($y!=driver()->primary),"auto_increment"=>($y==driver()->primary));}}}if($_POST["save"])$K=(array)$_POST["fields"]+($K?$K:array());edit_form($a,$o,$K,$_j,$m);}elseif(isset($_GET["create"])){$a=$_GET["create"];$Fg=driver()->partitionBy;$Ig=($Fg?driver()->partitionsInfo($a):array());$qh=referencable_primary($a);$md=array();foreach($qh +as$Di=>$n)$md[str_replace("`","``",$Di)."`".str_replace("`","``",$n["field"])]=$Di;$pg=array();$S=array();if($a!=""){$pg=fields($a);$S=table_status1($a);if(count($S)<2)$m=lang(11);}$K=$_POST;$K["fields"]=(array)$K["fields"];if($K["auto_increment_col"])$K["fields"][$K["auto_increment_col"]]["auto_increment"]=true;if($_POST)save_settings(array("comments"=>$_POST["comments"],"defaults"=>$_POST["defaults"]));if($_POST&&!process_fields($K["fields"])&&!$m){if($_POST["drop"])queries_redirect(substr(ME,0,-1),lang(178),drop_tables(array($a)));else{$o=array();$ua=array();$Fj=false;$kd=array();$og=reset($pg);$sa=" FIRST";foreach($K["fields"]as$y=>$n){$q=$md[$n["type"]];$qj=($q!==null?$qh[$q]:$n);if($n["field"]!=""){if(!$n["generated"])$n["default"]=null;$gh=process_field($n,$qj);$ua[]=array($n["orig"],$gh,$sa);if(!$og||$gh!==process_field($og,$og)){$o[]=array($n["orig"],$gh,$sa);if($n["orig"]!=""||$sa)$Fj=true;}if($q!==null)$kd[idf_escape($n["field"])]=($a!=""&&JUSH!="sqlite"?"ADD":" ").format_foreign_key(array('table'=>$md[$n["type"]],'source'=>array($n["field"]),'target'=>array($qj["field"]),'on_delete'=>$n["on_delete"],));$sa=" AFTER ".idf_escape($n["field"]);}elseif($n["orig"]!=""){$Fj=true;$o[]=array($n["orig"]);}if($n["orig"]!=""){$og=next($pg);if(!$og)$sa="";}}$E=array();if(in_array($K["partition_by"],$Fg)){foreach($K +as$y=>$X){if(preg_match('~^partition~',$y))$E[$y]=$X;}foreach($E["partition_names"]as$y=>$B){if($B==""){unset($E["partition_names"][$y]);unset($E["partition_values"][$y]);}}$E["partition_names"]=array_values($E["partition_names"]);$E["partition_values"]=array_values($E["partition_values"]);if($E==$Ig)$E=array();}elseif(preg_match("~partitioned~",$S["Create_options"]))$E=null;$qf=lang(179);if($a==""){cookie("adminer_engine",$K["Engine"]);$qf=lang(180);}$B=trim($K["name"]);queries_redirect(ME.(support("table")?"table=":"select=").urlencode($B),$qf,alter_table($a,$B,(JUSH=="sqlite"&&($Fj||$kd)?$ua:$o),$kd,($K["Comment"]!=$S["Comment"]?$K["Comment"]:null),($K["Engine"]&&$K["Engine"]!=$S["Engine"]?$K["Engine"]:""),($K["Collation"]&&$K["Collation"]!=$S["Collation"]?$K["Collation"]:""),($K["Auto_increment"]!=""?number($K["Auto_increment"]):""),$E));}}page_header(($a!=""?lang(45):lang(78)),$m,array("table"=>$a),h($a));if(!$_POST){$rj=driver()->types();$K=array("Engine"=>$_COOKIE["adminer_engine"],"fields"=>array(array("field"=>"","type"=>(isset($rj["int"])?"int":(isset($rj["integer"])?"integer":"")),"on_update"=>"")),"partition_names"=>array(""),);if($a!=""){$K=$S;$K["name"]=$a;$K["fields"]=array();if(!$_GET["auto_increment"])$K["Auto_increment"]="";foreach($pg +as$n){$n["generated"]=$n["generated"]?:(isset($n["default"])?"DEFAULT":"");$K["fields"][]=$n;}if($Fg){$K+=$Ig;$K["partition_names"][]="";$K["partition_values"][]="";}}}$lb=collations();if(is_array(reset($lb)))$lb=call_user_func_array('array_merge',array_values($lb));$zc=driver()->engines();foreach($zc +as$yc){if(!strcasecmp($yc,$K["Engine"])){$K["Engine"]=$yc;break;}}echo' +
    +

    +';if(support("columns")||$a==""){echo +lang(181).": \n",($zc?html_select("Engine",array(""=>"(".lang(182).")")+$zc,$K["Engine"]).on_help("event.target.value",1).script("qsl('select').onchange = helpClose;")."\n":"");if($lb)echo"".optionlist($lb)."\n",(preg_match("~sqlite|mssql~",JUSH)?"":"\n");echo"\n";}if(support("columns")){echo"

    \n","\n";edit_fields($K["fields"],$lb,"TABLE",$md);echo"
    \n",script("editFields();"),"
    \n

    \n",lang(52).": \n",checkbox("defaults",1,($_POST?$_POST["defaults"]:get_setting("defaults")),lang(183),"columnShow(this.checked, 5)","jsonly");$sb=($_POST?$_POST["comments"]:get_setting("comments"));echo(support("comment")?checkbox("comments",1,$sb,lang(51),"editingCommentsClick(this, true);","jsonly").' '.(preg_match('~\n~',$K["Comment"])?"":''):''),'

    + +';}echo' +';if($a!="")echo'',confirm(lang(184,$a));if($Fg&&(JUSH=='sql'||$a=="")){$Gg=preg_match('~RANGE|LIST~',$K["partition_by"]);print_fieldset("partition",lang(185),$K["partition_by"]);echo"

    ".html_select("partition_by",array_merge(array(""),$Fg),$K["partition_by"]).on_help("event.target.value.replace(/./, 'PARTITION BY \$&')",1).script("qsl('select').onchange = partitionByChange;"),"()\n",lang(186).": \n","\n","\n";foreach($K["partition_names"]as$y=>$X)echo'','\n\n";}echo +input_token(),'

    +';}elseif(isset($_GET["indexes"])){$a=$_GET["indexes"];$ee=array("PRIMARY","UNIQUE","INDEX");$S=table_status1($a,true);$be=driver()->indexAlgorithms($S);if(preg_match('~MyISAM|M?aria'.(min_version(5.6,'10.0.5')?'|InnoDB':'').'~i',$S["Engine"]))$ee[]="FULLTEXT";if(preg_match('~MyISAM|M?aria'.(min_version(5.7,'10.2.2')?'|InnoDB':'').'~i',$S["Engine"]))$ee[]="SPATIAL";$x=indexes($a);$o=fields($a);$G=array();if(JUSH=="mongo"){$G=$x["_id_"];unset($ee[0]);unset($x["_id_"]);}$K=$_POST;if($K)save_settings(array("index_options"=>$K["options"]));if($_POST&&!$m&&!$_POST["add"]&&!$_POST["drop_col"]){$b=array();foreach($K["indexes"]as$w){$B=$w["name"];if(in_array($w["type"],$ee)){$e=array();$Se=array();$cc=array();$ce=(support("partial_indexes")?$w["partial"]:"");$ae=(in_array($w["algorithm"],$be)?$w["algorithm"]:"");$O=array();ksort($w["columns"]);foreach($w["columns"]as$y=>$d){if($d!=""){$Re=idx($w["lengths"],$y);$ac=idx($w["descs"],$y);$O[]=($o[$d]?idf_escape($d):$d).($Re?"(".(+$Re).")":"").($ac?" DESC":"");$e[]=$d;$Se[]=($Re?:null);$cc[]=$ac;}}$Lc=$x[$B];if($Lc){ksort($Lc["columns"]);ksort($Lc["lengths"]);ksort($Lc["descs"]);if($w["type"]==$Lc["type"]&&array_values($Lc["columns"])===$e&&(!$Lc["lengths"]||array_values($Lc["lengths"])===$Se)&&array_values($Lc["descs"])===$cc&&$Lc["partial"]==$ce&&(!$be||$Lc["algorithm"]==$ae)){unset($x[$B]);continue;}}if($e)$b[]=array($w["type"],$B,$O,$ae,$ce);}}foreach($x +as$B=>$Lc)$b[]=array($Lc["type"],$B,"DROP");if(!$b)redirect(ME."table=".urlencode($a));queries_redirect(ME."table=".urlencode($a),lang(189),alter_indexes($a,$b));}page_header(lang(141),$m,array("table"=>$a),h($a));$Zc=array_keys($o);if($_POST["add"]){foreach($K["indexes"]as$y=>$w){if($w["columns"][count($w["columns"])]!="")$K["indexes"][$y]["columns"][]="";}$w=end($K["indexes"]);if($w["type"]||array_filter($w["columns"],'strlen'))$K["indexes"][]=array("columns"=>array(1=>""));}if(!$K){foreach($x +as$y=>$w){$x[$y]["name"]=$y;$x[$y]["columns"][]="";}$x[]=array("columns"=>array(1=>""));$K["indexes"]=$x;}$Se=(JUSH=="sql"||JUSH=="mssql");$ei=($_POST?$_POST["options"]:get_setting("index_options"));echo' +
    +
    + + + +';if($G){echo"
    ',lang(190);$Ud=" class='idxopts".($ei?"":" hidden")."'";if($be)echo"".lang(191).doc_link(array('sql'=>'create-index.html#create-index-storage-engine-index-types','mariadb'=>'storage-engine-index-types/','pgsql'=>'indexes-types.html',));echo'',lang(192).($Se?" (".lang(193).")":"");if($Se||support("descidx"))echo +checkbox("options",1,$ei,lang(113),"indexOptionsShow(this.checked)","jsonly")."\n";echo'',lang(194);if(support("partial_indexes"))echo"".lang(195);echo' +
    PRIMARY";foreach($G["columns"]as$y=>$d)echo +select_input(" disabled",$Zc,$d)," ";echo"\n";}$_e=1;foreach($K["indexes"]as$w){if(!$_POST["drop_col"]||$_e!=key($_POST["drop_col"])){echo"
    ".html_select("indexes[$_e][type]",array(-1=>"")+$ee,$w["type"],($_e==count($K["indexes"])?"indexesAddRow.call(this);":""),"label-type");if($be)echo"".html_select("indexes[$_e][algorithm]",array_merge(array(""),$be),$w['algorithm'],"label-algorithm");echo"";ksort($w["columns"]);$t=1;foreach($w["columns"]as$y=>$d){echo"".select_input(" name='indexes[$_e][columns][$t]' title='".lang(49)."'",($o&&($d==""||$o[$d])?array_combine($Zc,$Zc):array()),$d,"partial(".($t==count($w["columns"])?"indexesAddColumn":"indexesChangeColumn").", '".js_escape(JUSH=="sql"?"":$_GET["indexes"]."_")."')"),"",($Se?"":""),(support("descidx")?checkbox("indexes[$_e][descs][$t]",1,idx($w["descs"],$y),lang(60)):"")," ";$t++;}echo"\n";if(support("partial_indexes"))echo"\n";echo"".icon("cross","drop_col[$_e]","x",lang(117)).script("qsl('button').onclick = partial(editingRemoveRow, 'indexes\$1[type]');");}$_e++;}echo'
    +
    +

    + +',input_token(),'

    +';}elseif(isset($_GET["database"])){$K=$_POST;if($_POST&&!$m&&!$_POST["add"]){$B=trim($K["name"]);if($_POST["drop"]){$_GET["db"]="";queries_redirect(remove_from_uri("db|database"),lang(196),drop_databases(array(DB)));}elseif(DB!==$B){if(DB!=""){$_GET["db"]=$B;queries_redirect(preg_replace('~\bdb=[^&]*&~','',ME)."db=".urlencode($B),lang(197),rename_database($B,$K["collation"]));}else{$j=explode("\n",str_replace("\r","",$B));$xi=true;$Le="";foreach($j +as$k){if(count($j)==1||$k!=""){if(!create_database($k,$K["collation"]))$xi=false;$Le=$k;}}restart_session();set_session("dbs",null);queries_redirect(ME."db=".urlencode($Le),lang(198),$xi);}}else{if(!$K["collation"])redirect(substr(ME,0,-1));query_redirect("ALTER DATABASE ".idf_escape($B).(preg_match('~^[a-z0-9_]+$~i',$K["collation"])?" COLLATE $K[collation]":""),substr(ME,0,-1),lang(199));}}page_header(DB!=""?lang(68):lang(121),$m,array(),h(DB));$lb=collations();$B=DB;if($_POST)$B=$K["name"];elseif(DB!="")$K["collation"]=db_collation(DB,$lb);elseif(JUSH=="sql"){foreach(get_vals("SHOW GRANTS")as$vd){if(preg_match('~ ON (`(([^\\\\`]|``|\\\\.)*)%`\.\*)?~',$vd,$A)&&$A[1]){$B=stripcslashes(idf_unescape("`$A[2]`"));break;}}}echo' +
    +

    +',($_POST["add"]||strpos($B,"\n")?'
    ':'')."\n".($lb?html_select("collation",array(""=>"(".lang(107).")")+$lb,$K["collation"]).doc_link(array('sql'=>"charset-charsets.html",'mariadb'=>"supported-character-sets-and-collations/",'mssql'=>"relational-databases/system-functions/sys-fn-helpcollations-transact-sql",)):""),' +';if(DB!="")echo"".confirm(lang(184,DB))."\n";elseif(!$_POST["add"]&&$_GET["db"]=="")echo +icon("plus","add[0]","+",lang(114))."\n";echo +input_token(),'

    +';}elseif(isset($_GET["scheme"])){$K=$_POST;if($_POST&&!$m){$_=preg_replace('~ns=[^&]*&~','',ME)."ns=";if($_POST["drop"])query_redirect("DROP SCHEMA ".idf_escape($_GET["ns"]),$_,lang(200));else{$B=trim($K["name"]);$_ +.=urlencode($B);if($_GET["ns"]=="")query_redirect("CREATE SCHEMA ".idf_escape($B),$_,lang(201));elseif($_GET["ns"]!=$B)query_redirect("ALTER SCHEMA ".idf_escape($_GET["ns"])." RENAME TO ".idf_escape($B),$_,lang(202));else +redirect($_);}}page_header($_GET["ns"]!=""?lang(69):lang(70),$m);if(!$K)$K["name"]=$_GET["ns"];echo' +
    +

    + +';if($_GET["ns"]!="")echo"".confirm(lang(184,$_GET["ns"]))."\n";echo +input_token(),'

    +';}elseif(isset($_GET["call"])){$ca=($_GET["name"]?:$_GET["call"]);page_header(lang(203).": ".h($ca),$m);$Dh=routine($_GET["call"],(isset($_GET["callf"])?"FUNCTION":"PROCEDURE"));$Xd=array();$ug=array();foreach($Dh["fields"]as$t=>$n){if(substr($n["inout"],-3)=="OUT"&&JUSH=='sql')$ug[$t]="@".idf_escape($n["field"])." AS ".idf_escape($n["field"]);if(!$n["inout"]||substr($n["inout"],0,2)=="IN")$Xd[]=$t;}if(!$m&&$_POST){$Ua=array();foreach($Dh["fields"]as$y=>$n){$X="";if(in_array($y,$Xd)){$X=process_input($n);if($X===false)$X="''";if(isset($ug[$y]))connection()->query("SET @".idf_escape($n["field"])." = $X");}if(isset($ug[$y]))$Ua[]="@".idf_escape($n["field"]);elseif(in_array($y,$Xd))$Ua[]=$X;}$H=(isset($_GET["callf"])?"SELECT ":"CALL ").(idx($Dh["returns"],"type")=="record"?"* FROM ":"").table($ca)."(".implode(", ",$Ua).")";$si=microtime(true);$I=connection()->multi_query($H);$qa=connection()->affected_rows;echo +adminer()->selectQuery($H,$si,!$I);if(!$I)echo"

    ".error()."\n";else{$h=connect();if($h)$h->select_db(DB);do{$I=connection()->store_result();if(is_object($I))print_select_result($I,$h);else +echo"

    ".lang(204,$qa)." ".@date("H:i:s")."\n";}while(connection()->next_result());if($ug)print_select_result(connection()->query("SELECT ".implode(", ",$ug)));}}echo' +

    +';if($Xd){echo"\n";foreach($Xd +as$y){$n=$Dh["fields"][$y];$B=$n["field"];echo"
    ".adminer()->fieldName($n);$Y=idx($_POST["fields"],$B);if($Y!=""){if($n["type"]=="set")$Y=implode(",",$Y);}input($n,$Y,idx($_POST["function"],$B,""));echo"\n";}echo"
    \n";}echo'

    + +',input_token(),'

    + +
    +';function
    +pre_tr($Hh){return
    +preg_replace('~^~m','',preg_replace('~\|~','',preg_replace('~\|$~m',"",rtrim($Hh))));}$R='(\+--[-+]+\+\n)';$K='(\| .* \|\n)';echo
    +preg_replace_callback("~^$R?$K$R?($K*)$R?~m",function($A){$dd=pre_tr($A[2]);return"\n".($A[1]?"$dd\n":$dd).pre_tr($A[4])."\n
    ";},preg_replace('~(\n( -|mysql)> )(.+)~',"\\1\\3",preg_replace('~(.+)\n---+\n~',"\\1\n",h($Dh['comment']))));echo'
    +';}elseif(isset($_GET["foreign"])){$a=$_GET["foreign"];$B=$_GET["name"];$K=$_POST;if($_POST&&!$m&&!$_POST["add"]&&!$_POST["change"]&&!$_POST["change-js"]){if(!$_POST["drop"]){$K["source"]=array_filter($K["source"],'strlen');ksort($K["source"]);$Mi=array();foreach($K["source"]as$y=>$X)$Mi[$y]=$K["target"][$y];$K["target"]=$Mi;}if(JUSH=="sqlite")$I=recreate_table($a,$a,array(),array(),array(" $B"=>($K["drop"]?"":" ".format_foreign_key($K))));else{$b="ALTER TABLE ".table($a);$I=($B==""||queries("$b DROP ".(JUSH=="sql"?"FOREIGN KEY ":"CONSTRAINT ").idf_escape($B)));if(!$K["drop"])$I=queries("$b ADD".format_foreign_key($K));}queries_redirect(ME."table=".urlencode($a),($K["drop"]?lang(205):($B!=""?lang(206):lang(207))),$I);if(!$K["drop"])$m=lang(208);}page_header(lang(209),$m,array("table"=>$a),h($a));if($_POST){ksort($K["source"]);if($_POST["add"])$K["source"][]="";elseif($_POST["change"]||$_POST["change-js"])$K["target"]=array();}elseif($B!=""){$md=foreign_keys($a);$K=$md[$B];$K["source"][]="";}else{$K["table"]=$a;$K["source"]=array("");}echo' +
    +';$ji=array_keys(fields($a));if($K["db"]!="")connection()->select_db($K["db"]);if($K["ns"]!=""){$qg=get_schema();set_schema($K["ns"]);}$ph=array_keys(array_filter(table_status('',true),'Adminer\fk_support'));$Mi=array_keys(fields(in_array($K["table"],$ph)?$K["table"]:reset($ph)));$ag="this.form['change-js'].value = '1'; this.form.submit();";echo"

    \n";if(support("scheme")){$Kh=array_filter(adminer()->schemas(),function($Jh){return!preg_match('~^information_schema$~i',$Jh);});echo"";if($K["ns"]!="")set_schema($qg);}elseif(JUSH!="sqlite"){$Sb=array();foreach(adminer()->databases()as$k){if(!information_schema($k))$Sb[]=$k;}echo"";}echo +input_hidden("change-js"),'

    + + +';$_e=0;foreach($K["source"]as$y=>$X){echo"","
    ',lang(143),'',lang(144),'
    ".html_select("source[".(+$y)."]",array(-1=>"")+$ji,$X,($_e==count($K["source"])-1?"foreignAddRow.call(this);":""),"label-source"),"".html_select("target[".(+$y)."]",$Mi,idx($K["target"],$y),"","label-target");$_e++;}echo'
    +

    + + +',(DRIVER==='pgsql'?html_select("deferrable",array('NOT DEFERRABLE','DEFERRABLE','DEFERRABLE INITIALLY DEFERRED'),$K["deferrable"]).' ':''),doc_link(array('sql'=>"innodb-foreign-key-constraints.html",'mariadb'=>"foreign-keys/",'pgsql'=>"sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES",'mssql'=>"t-sql/statements/create-table-transact-sql",'oracle'=>"SQLRF01111",)),'

    + +

    +';if($B!="")echo'',confirm(lang(184,$B));echo +input_token(),'

    +';}elseif(isset($_GET["view"])){$a=$_GET["view"];$K=$_POST;$rg="VIEW";if(JUSH=="pgsql"&&$a!=""){$P=table_status1($a);$rg=strtoupper($P["Engine"]);}if($_POST&&!$m){$B=trim($K["name"]);$ya=" AS\n$K[select]";$We=ME."table=".urlencode($B);$qf=lang(213);$U=($_POST["materialized"]?"MATERIALIZED VIEW":"VIEW");if(!$_POST["drop"]&&$a==$B&&JUSH!="sqlite"&&$U=="VIEW"&&$rg=="VIEW")query_redirect((JUSH=="mssql"?"ALTER":"CREATE OR REPLACE")." VIEW ".table($B).$ya,$We,$qf);else{$Oi=$B."_adminer_".uniqid();drop_create("DROP $rg ".table($a),"CREATE $U ".table($B).$ya,"DROP $U ".table($B),"CREATE $U ".table($Oi).$ya,"DROP $U ".table($Oi),($_POST["drop"]?substr(ME,0,-1):$We),lang(214),$qf,lang(215),$a,$B);}}if(!$_POST&&$a!=""){$K=view($a);$K["name"]=$a;$K["materialized"]=($rg!="VIEW");if(!$m)$m=error();}page_header(($a!=""?lang(44):lang(216)),$m,array("table"=>$a),h($a));echo' +
    +

    ',lang(194),': +',(support("materializedview")?" ".checkbox("materialized",1,$K["materialized"],lang(137)):""),'

    ';textarea("select",$K["select"]);echo'

    + +';if($a!="")echo'',confirm(lang(184,$a));echo +input_token(),'

    +';}elseif(isset($_GET["event"])){$aa=$_GET["event"];$re=array("YEAR","QUARTER","MONTH","DAY","HOUR","MINUTE","WEEK","SECOND","YEAR_MONTH","DAY_HOUR","DAY_MINUTE","DAY_SECOND","HOUR_MINUTE","HOUR_SECOND","MINUTE_SECOND");$ti=array("ENABLED"=>"ENABLE","DISABLED"=>"DISABLE","SLAVESIDE_DISABLED"=>"DISABLE ON SLAVE");$K=$_POST;if($_POST&&!$m){if($_POST["drop"])query_redirect("DROP EVENT ".idf_escape($aa),substr(ME,0,-1),lang(217));elseif(in_array($K["INTERVAL_FIELD"],$re)&&isset($ti[$K["STATUS"]])){$Ih="\nON SCHEDULE ".($K["INTERVAL_VALUE"]?"EVERY ".q($K["INTERVAL_VALUE"])." $K[INTERVAL_FIELD]".($K["STARTS"]?" STARTS ".q($K["STARTS"]):"").($K["ENDS"]?" ENDS ".q($K["ENDS"]):""):"AT ".q($K["STARTS"]))." ON COMPLETION".($K["ON_COMPLETION"]?"":" NOT")." PRESERVE";queries_redirect(substr(ME,0,-1),($aa!=""?lang(218):lang(219)),queries(($aa!=""?"ALTER EVENT ".idf_escape($aa).$Ih.($aa!=$K["EVENT_NAME"]?"\nRENAME TO ".idf_escape($K["EVENT_NAME"]):""):"CREATE EVENT ".idf_escape($K["EVENT_NAME"]).$Ih)."\n".$ti[$K["STATUS"]]." COMMENT ".q($K["EVENT_COMMENT"]).rtrim(" DO\n$K[EVENT_DEFINITION]",";").";"));}}page_header(($aa!=""?lang(220).": ".h($aa):lang(221)),$m);if(!$K&&$aa!=""){$L=get_rows("SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = ".q(DB)." AND EVENT_NAME = ".q($aa));$K=reset($L);}echo' +
    + +
    ',lang(194),' +
    ',lang(222),' +
    ',lang(223),' +
    ',lang(224),' ',html_select("INTERVAL_FIELD",$re,$K["INTERVAL_FIELD"]),'
    ',lang(124),'',html_select("STATUS",$ti,$K["STATUS"]),'
    ',lang(51),' +
    ',checkbox("ON_COMPLETION","PRESERVE",$K["ON_COMPLETION"]=="PRESERVE",lang(225)),'
    +

    ';textarea("EVENT_DEFINITION",$K["EVENT_DEFINITION"]);echo'

    + +';if($aa!="")echo'',confirm(lang(184,$aa));echo +input_token(),'

    +';}elseif(isset($_GET["procedure"])){$ca=($_GET["name"]?:$_GET["procedure"]);$Dh=(isset($_GET["function"])?"FUNCTION":"PROCEDURE");$K=$_POST;$K["fields"]=(array)$K["fields"];if($_POST&&!process_fields($K["fields"])&&!$m){$ng=routine($_GET["procedure"],$Dh);$Oi="$K[name]_adminer_".uniqid();foreach($K["fields"]as$y=>$n){if($n["field"]=="")unset($K["fields"][$y]);}drop_create("DROP $Dh ".routine_id($ca,$ng),create_routine($Dh,$K),"DROP $Dh ".routine_id($K["name"],$K),create_routine($Dh,array("name"=>$Oi)+$K),"DROP $Dh ".routine_id($Oi,$K),substr(ME,0,-1),lang(226),lang(227),lang(228),$ca,$K["name"]);}page_header(($ca!=""?(isset($_GET["function"])?lang(229):lang(230)).": ".h($ca):(isset($_GET["function"])?lang(231):lang(232))),$m);if(!$_POST){if($ca=="")$K["language"]="sql";else{$K=routine($_GET["procedure"],$Dh);$K["name"]=$ca;}}$lb=get_vals("SHOW CHARACTER SET");sort($lb);$Eh=routine_languages();echo($lb?"".optionlist($lb)."":""),' +
    +

    ',lang(194),': +',($Eh?"\n":""),' +

    + +';edit_fields($K["fields"],$lb,$Dh);if(isset($_GET["function"])){echo"
    ".lang(233);edit_type("returns",(array)$K["returns"],$lb,array(),(JUSH=="pgsql"?array("void","trigger"):array()));}echo'
    +',script("editFields();"),'
    +

    ';textarea("definition",$K["definition"],20);echo'

    + +';if($ca!="")echo'',confirm(lang(184,$ca));echo +input_token(),'

    +';}elseif(isset($_GET["sequence"])){$ea=$_GET["sequence"];$K=$_POST;if($_POST&&!$m){$_=substr(ME,0,-1);$B=trim($K["name"]);if($_POST["drop"])query_redirect("DROP SEQUENCE ".idf_escape($ea),$_,lang(234));elseif($ea=="")query_redirect("CREATE SEQUENCE ".idf_escape($B),$_,lang(235));elseif($ea!=$B)query_redirect("ALTER SEQUENCE ".idf_escape($ea)." RENAME TO ".idf_escape($B),$_,lang(236));else +redirect($_);}page_header($ea!=""?lang(237).": ".h($ea):lang(238),$m);if(!$K)$K["name"]=$ea;echo' +
    +

    + +';if($ea!="")echo"".confirm(lang(184,$ea))."\n";echo +input_token(),'

    +';}elseif(isset($_GET["type"])){$fa=$_GET["type"];$K=$_POST;if($_POST&&!$m){$_=substr(ME,0,-1);if($_POST["drop"])query_redirect("DROP TYPE ".idf_escape($fa),$_,lang(239));else +query_redirect("CREATE TYPE ".idf_escape(trim($K["name"]))." $K[as]",$_,lang(240));}page_header($fa!=""?lang(241).": ".h($fa):lang(242),$m);if(!$K)$K["as"]="AS ";echo' +
    +

    +';if($fa!=""){$rj=driver()->types();$Cc=type_values($rj[$fa]);if($Cc)echo"ENUM (".h($Cc).")\n

    ";echo"".confirm(lang(184,$fa))."\n";}else{echo +lang(194).": \n",doc_link(array('pgsql'=>"datatype-enum.html",),"?");textarea("as",$K["as"]);echo"

    \n";}echo +input_token(),'

    +';}elseif(isset($_GET["check"])){$a=$_GET["check"];$B=$_GET["name"];$K=$_POST;if($K&&!$m){if(JUSH=="sqlite")$I=recreate_table($a,$a,array(),array(),array(),"",array(),"$B",($K["drop"]?"":$K["clause"]));else{$I=($B==""||queries("ALTER TABLE ".table($a)." DROP CONSTRAINT ".idf_escape($B)));if(!$K["drop"])$I=queries("ALTER TABLE ".table($a)." ADD".($K["name"]!=""?" CONSTRAINT ".idf_escape($K["name"]):"")." CHECK ($K[clause])");}queries_redirect(ME."table=".urlencode($a),($K["drop"]?lang(243):($B!=""?lang(244):lang(245))),$I);}page_header(($B!=""?lang(246).": ".h($B):lang(148)),$m,array("table"=>$a));if(!$K){$cb=driver()->checkConstraints($a);$K=array("name"=>$B,"clause"=>$cb[$B]);}echo' +
    +

    ';if(JUSH!="sqlite")echo +lang(194).': ';echo +doc_link(array('sql'=>"create-table-check-constraints.html",'mariadb'=>"constraint/",'pgsql'=>"ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS",'mssql'=>"relational-databases/tables/create-check-constraints",'sqlite'=>"lang_createtable.html#check_constraints",),"?"),'

    ';textarea("clause",$K["clause"]);echo'

    +';if($B!="")echo'',confirm(lang(184,$B));echo +input_token(),'

    +';}elseif(isset($_GET["trigger"])){$a=$_GET["trigger"];$B="$_GET[name]";$nj=trigger_options();$K=(array)trigger($B,$a)+array("Trigger"=>$a."_bi");if($_POST){if(!$m&&in_array($_POST["Timing"],$nj["Timing"])&&in_array($_POST["Event"],$nj["Event"])&&in_array($_POST["Type"],$nj["Type"])){$Xf=" ON ".table($a);$jc="DROP TRIGGER ".idf_escape($B).(JUSH=="pgsql"?$Xf:"");$We=ME."table=".urlencode($a);if($_POST["drop"])query_redirect($jc,$We,lang(247));else{if($B!="")queries($jc);queries_redirect($We,($B!=""?lang(248):lang(249)),queries(create_trigger($Xf,$_POST)));if($B!="")queries(create_trigger($Xf,$K+array("Type"=>reset($nj["Type"]))));}}$K=$_POST;}page_header(($B!=""?lang(250).": ".h($B):lang(251)),$m,array("table"=>$a));echo' +
    + +
    ',lang(252),'',html_select("Timing",$nj["Timing"],$K["Timing"],"triggerChange(/^".preg_quote($a,"/")."_[ba][iud]$/, '".js_escape($a)."', this.form);"),'
    ',lang(253),'',html_select("Event",$nj["Event"],$K["Event"],"this.form['Timing'].onchange();"),(in_array("UPDATE OF",$nj["Event"])?" ":""),'
    ',lang(50),'',html_select("Type",$nj["Type"],$K["Type"]),'
    +

    ',lang(194),': +',script("qs('#form')['Timing'].onchange();"),'

    ';textarea("Statement",$K["Statement"]);echo'

    + +';if($B!="")echo'',confirm(lang(184,$B));echo +input_token(),'

    +';}elseif(isset($_GET["user"])){$ga=$_GET["user"];$eh=array(""=>array("All privileges"=>""));foreach(get_rows("SHOW PRIVILEGES")as$K){foreach(explode(",",($K["Privilege"]=="Grant option"?"":$K["Context"]))as$Bb)$eh[$Bb][$K["Privilege"]]=$K["Comment"];}$eh["Server Admin"]+=$eh["File access on server"];$eh["Databases"]["Create routine"]=$eh["Procedures"]["Create routine"];unset($eh["Procedures"]["Create routine"]);$eh["Columns"]=array();foreach(array("Select","Insert","Update","References")as$X)$eh["Columns"][$X]=$eh["Tables"][$X];unset($eh["Server Admin"]["Usage"]);foreach($eh["Tables"]as$y=>$X)unset($eh["Databases"][$y]);$Ff=array();if($_POST){foreach($_POST["objects"]as$y=>$X)$Ff[$X]=(array)$Ff[$X]+idx($_POST["grants"],$y,array());}$wd=array();$Vf="";if(isset($_GET["host"])&&($I=connection()->query("SHOW GRANTS FOR ".q($ga)."@".q($_GET["host"])))){while($K=$I->fetch_row()){if(preg_match('~GRANT (.*) ON (.*) TO ~',$K[0],$A)&&preg_match_all('~ *([^(,]*[^ ,(])( *\([^)]+\))?~',$A[1],$df,PREG_SET_ORDER)){foreach($df +as$X){if($X[1]!="USAGE")$wd["$A[2]$X[2]"][$X[1]]=true;if(preg_match('~ WITH GRANT OPTION~',$K[0]))$wd["$A[2]$X[2]"]["GRANT OPTION"]=true;}}if(preg_match("~ IDENTIFIED BY PASSWORD '([^']+)~",$K[0],$A))$Vf=$A[1];}}if($_POST&&!$m){$Wf=(isset($_GET["host"])?q($ga)."@".q($_GET["host"]):"''");if($_POST["drop"])query_redirect("DROP USER $Wf",ME."privileges=",lang(254));else{$Hf=q($_POST["user"])."@".q($_POST["host"]);$Jg=$_POST["pass"];if($Jg!=''&&!$_POST["hashed"]&&!min_version(8)){$Jg=get_val("SELECT PASSWORD(".q($Jg).")");$m=!$Jg;}$Gb=false;if(!$m){if($Wf!=$Hf){$Gb=queries((min_version(5)?"CREATE USER":"GRANT USAGE ON *.* TO")." $Hf IDENTIFIED BY ".(min_version(8)?"":"PASSWORD ").q($Jg));$m=!$Gb;}elseif($Jg!=$Vf)queries("SET PASSWORD FOR $Hf = ".q($Jg));}if(!$m){$Ah=array();foreach($Ff +as$Pf=>$vd){if(isset($_GET["grant"]))$vd=array_filter($vd);$vd=array_keys($vd);if(isset($_GET["grant"]))$Ah=array_diff(array_keys(array_filter($Ff[$Pf],'strlen')),$vd);elseif($Wf==$Hf){$Tf=array_keys((array)$wd[$Pf]);$Ah=array_diff($Tf,$vd);$vd=array_diff($vd,$Tf);unset($wd[$Pf]);}if(preg_match('~^(.+)\s*(\(.*\))?$~U',$Pf,$A)&&(!grant("REVOKE",$Ah,$A[2]," ON $A[1] FROM $Hf")||!grant("GRANT",$vd,$A[2]," ON $A[1] TO $Hf"))){$m=true;break;}}}if(!$m&&isset($_GET["host"])){if($Wf!=$Hf)queries("DROP USER $Wf");elseif(!isset($_GET["grant"])){foreach($wd +as$Pf=>$Ah){if(preg_match('~^(.+)(\(.*\))?$~U',$Pf,$A))grant("REVOKE",array_keys($Ah),$A[2]," ON $A[1] FROM $Hf");}}}queries_redirect(ME."privileges=",(isset($_GET["host"])?lang(255):lang(256)),!$m);if($Gb)connection()->query("DROP USER $Hf");}}page_header((isset($_GET["host"])?lang(36).": ".h("$ga@$_GET[host]"):lang(156)),$m,array("privileges"=>array('',lang(72))));$K=$_POST;if($K)$wd=$Ff;else{$K=$_GET+array("host"=>get_val("SELECT SUBSTRING_INDEX(CURRENT_USER, '@', -1)"));$K["pass"]=$Vf;if($Vf!="")$K["hashed"]=true;$wd[(DB==""||$wd?"":idf_escape(addcslashes(DB,"%_\\"))).".*"]=array();}echo'
    + +
    ',lang(34),' +
    ',lang(36),' +
    ',lang(37),' +',($K["hashed"]?"":script("typePassword(qs('#pass'));")),(min_version(8)?"":checkbox("hashed",1,$K["hashed"],lang(257),"typePassword(this.form['pass'], this.checked);")),'
    + +',"\n","\n";foreach(array(""=>"","Server Admin"=>lang(34),"Databases"=>lang(38),"Tables"=>lang(139),"Columns"=>lang(49),"Procedures"=>lang(258),)as$Bb=>$ac){foreach((array)$eh[$Bb]as$dh=>$qb){echo"$ac'.h($dh);$t=0;foreach($wd +as$Pf=>$vd){$B="'grants[$t][".h(strtoupper($dh))."]'";$Y=$vd[strtoupper($dh)];if($Bb=="Server Admin"&&$Pf!=(isset($wd["*.*"])?"*.*":".*"))echo"
    ".lang(72).doc_link(array('sql'=>"grant.html#priv_level"));$t=0;foreach($wd +as$Pf=>$vd){echo''.($Pf!="*.*"?"":input_hidden("objects[$t]","*.*")."*.*");$t++;}echo"
    ";elseif(isset($_GET["grant"]))echo"";else +echo"";$t++;}}}echo"
    \n",'

    + +';if(isset($_GET["host"]))echo'',confirm(lang(184,"$ga@$_GET[host]"));echo +input_token(),'

    +';}elseif(isset($_GET["processlist"])){if(support("kill")){if($_POST&&!$m){$Ge=0;foreach((array)$_POST["kill"]as$X){if(adminer()->killProcess($X))$Ge++;}queries_redirect(ME."processlist=",lang(261,$Ge),$Ge||!$_POST["kill"]);}}page_header(lang(122),$m);echo' +
    +
    + +',script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});");$t=-1;foreach(adminer()->processList()as$t=>$K){if(!$t){echo"".(support("kill")?"\n";}echo"".(support("kill")?"
    ":"");foreach($K +as$y=>$X)echo"$y".doc_link(array('sql'=>"show-processlist.html#processlist_".strtolower($y),'pgsql'=>"monitoring-stats.html#PG-STAT-ACTIVITY-VIEW",'oracle'=>"REFRN30223",));echo"
    ".checkbox("kill[]",$K[JUSH=="sql"?"Id":"pid"],0):"");foreach($K +as$y=>$X)echo"".((JUSH=="sql"&&$y=="Info"&&preg_match("~Query|Killed~",$K["Command"])&&$X!="")||(JUSH=="pgsql"&&$y=="current_query"&&$X!="")||(JUSH=="oracle"&&$y=="sql_text"&&$X!="")?"".shorten_utf8($X,100,"").' '.lang(262).'':h($X));echo"\n";}echo'
    +
    +

    +';if(support("kill"))echo($t+1)."/".lang(263,max_connections()),"

    \n";echo +input_token(),'

    +',script("tableCheck();");}elseif(isset($_GET["select"])){$a=$_GET["select"];$S=table_status1($a);$x=indexes($a);$o=fields($a);$md=column_foreign_keys($a);$Rf=$S["Oid"];$pa=get_settings("adminer_import");$Bh=array();$e=array();$Ph=array();$jg=array();$Si="";foreach($o +as$y=>$n){$B=adminer()->fieldName($n);$Df=html_entity_decode(strip_tags($B),ENT_QUOTES);if(isset($n["privileges"]["select"])&&$B!=""){$e[$y]=$Df;if(is_shortable($n))$Si=adminer()->selectLengthProcess();}if(isset($n["privileges"]["where"])&&$B!="")$Ph[$y]=$Df;if(isset($n["privileges"]["order"])&&$B!="")$jg[$y]=$Df;$Bh+=$n["privileges"];}list($M,$xd)=adminer()->selectColumnsProcess($e,$x);$M=array_unique($M);$xd=array_unique($xd);$ve=count($xd)selectSearchProcess($o,$x);$ig=adminer()->selectOrderProcess($o,$x);$z=adminer()->selectLimitProcess();if($_GET["val"]&&is_ajax()){header("Content-Type: text/plain; charset=utf-8");foreach($_GET["val"]as$wj=>$K){$ya=convert_field($o[key($K)]);$M=array($ya?:idf_escape(key($K)));$Z[]=where_check($wj,$o);$J=driver()->select($a,$M,$Z,$M);if($J)echo +first($J->fetch_row());}exit;}$G=$yj=array();foreach($x +as$w){if($w["type"]=="PRIMARY"){$G=array_flip($w["columns"]);$yj=($M?$G:array());foreach($yj +as$y=>$X){if(in_array(idf_escape($y),$M))unset($yj[$y]);}break;}}if($Rf&&!$G){$G=$yj=array($Rf=>0);$x[]=array("type"=>"PRIMARY","columns"=>array($Rf));}if($_POST&&!$m){$Yj=$Z;if(!$_POST["all"]&&is_array($_POST["check"])){$cb=array();foreach($_POST["check"]as$Ya)$cb[]=where_check($Ya,$o);$Yj[]="((".implode(") OR (",$cb)."))";}$Yj=($Yj?"\nWHERE ".implode(" AND ",$Yj):"");if($_POST["export"]){save_settings(array("output"=>$_POST["output"],"format"=>$_POST["format"]),"adminer_import");dump_headers($a);adminer()->dumpTable($a,"");$qd=($M?implode(", ",$M):"*").convert_fields($e,$o,$M)."\nFROM ".table($a);$zd=($xd&&$ve?"\nGROUP BY ".implode(", ",$xd):"").($ig?"\nORDER BY ".implode(", ",$ig):"");$H="SELECT $qd$Yj$zd";if(is_array($_POST["check"])&&!$G){$uj=array();foreach($_POST["check"]as$X)$uj[]="(SELECT".limit($qd,"\nWHERE ".($Z?implode(" AND ",$Z)." AND ":"").where_check($X,$o).$zd,1).")";$H=implode(" UNION ALL ",$uj);}adminer()->dumpData($a,"table",$H);adminer()->dumpFooter();exit;}if(!adminer()->selectEmailProcess($Z,$md)){if($_POST["save"]||$_POST["delete"]){$I=true;$qa=0;$O=array();if(!$_POST["delete"]){foreach($_POST["fields"]as$B=>$X){$X=process_input($o[$B]);if($X!==null&&($_POST["clone"]||$X!==false))$O[idf_escape($B)]=($X!==false?$X:idf_escape($B));}}if($_POST["delete"]||$O){$H=($_POST["clone"]?"INTO ".table($a)." (".implode(", ",array_keys($O)).")\nSELECT ".implode(", ",$O)."\nFROM ".table($a):"");if($_POST["all"]||($G&&is_array($_POST["check"]))||$ve){$I=($_POST["delete"]?driver()->delete($a,$Yj):($_POST["clone"]?queries("INSERT $H$Yj".driver()->insertReturning($a)):driver()->update($a,$O,$Yj)));$qa=connection()->affected_rows;if(is_object($I))$qa+=$I->num_rows;}else{foreach((array)$_POST["check"]as$X){$Xj="\nWHERE ".($Z?implode(" AND ",$Z)." AND ":"").where_check($X,$o);$I=($_POST["delete"]?driver()->delete($a,$Xj,1):($_POST["clone"]?queries("INSERT".limit1($a,$H,$Xj)):driver()->update($a,$O,$Xj,1)));if(!$I)break;$qa+=connection()->affected_rows;}}}$qf=lang(265,$qa);if($_POST["clone"]&&$I&&$qa==1){$Me=last_id($I);if($Me)$qf=lang(177," $Me");}queries_redirect(remove_from_uri($_POST["all"]&&$_POST["delete"]?"page":""),$qf,$I);if(!$_POST["delete"]){$Vg=(array)$_POST["fields"];edit_form($a,array_intersect_key($o,$Vg),$Vg,!$_POST["clone"],$m);page_footer();exit;}}elseif(!$_POST["import"]){if(!$_POST["val"])$m=lang(266);else{$I=true;$qa=0;foreach($_POST["val"]as$wj=>$K){$O=array();foreach($K +as$y=>$X){$y=bracket_escape($y,true);$O[idf_escape($y)]=(preg_match('~char|text~',$o[$y]["type"])||$X!=""?adminer()->processInput($o[$y],$X):"NULL");}$I=driver()->update($a,$O," WHERE ".($Z?implode(" AND ",$Z)." AND ":"").where_check($wj,$o),($ve||$G?0:1)," ");if(!$I)break;$qa+=connection()->affected_rows;}queries_redirect(remove_from_uri(),lang(265,$qa),$I);}}elseif(!is_string($ad=get_file("csv_file",true)))$m=upload_error($ad);elseif(!preg_match('~~u',$ad))$m=lang(267);else{save_settings(array("output"=>$pa["output"],"format"=>$_POST["separator"]),"adminer_import");$I=true;$mb=array_keys($o);preg_match_all('~(?>"[^"]*"|[^"\r\n]+)+~',$ad,$df);$qa=count($df[0]);driver()->begin();$Vh=($_POST["separator"]=="csv"?",":($_POST["separator"]=="tsv"?"\t":";"));$L=array();foreach($df[0]as$y=>$X){preg_match_all("~((?>\"[^\"]*\")+|[^$Vh]*)$Vh~",$X.$Vh,$ef);if(!$y&&!array_diff($ef[1],$mb)){$mb=$ef[1];$qa--;}else{$O=array();foreach($ef[1]as$t=>$jb)$O[idf_escape($mb[$t])]=($jb==""&&$o[$mb[$t]]["null"]?"NULL":q(preg_match('~^".*"$~s',$jb)?str_replace('""','"',substr($jb,1,-1)):$jb));$L[]=$O;}}$I=(!$L||driver()->insertUpdate($a,$L,$G));if($I)driver()->commit();queries_redirect(remove_from_uri("page"),lang(268,$qa),$I);driver()->rollback();}}}$Di=adminer()->tableName($S);if(is_ajax()){page_headers();ob_start();}else +page_header(lang(54).": $Di",$m);$O=null;if(isset($Bh["insert"])||!support("table")){$_g=array();foreach((array)$_GET["where"]as$X){if(isset($md[$X["col"]])&&count($md[$X["col"]])==1&&($X["op"]=="="||(!$X["op"]&&(is_array($X["val"])||!preg_match('~[_%]~',$X["val"])))))$_g["set"."[".bracket_escape($X["col"])."]"]=$X["val"];}$O=$_g?"&".http_build_query($_g):"";}adminer()->selectLinks($S,$O);if(!$e&&support("table"))echo"

    ".lang(269).($o?".":": ".error())."\n";else{echo"

    \n","
    ";hidden_fields_get();echo(DB!=""?input_hidden("db",DB).(isset($_GET["ns"])?input_hidden("ns",$_GET["ns"]):""):""),input_hidden("select",$a),"
    \n";adminer()->selectColumnsPrint($M,$e);adminer()->selectSearchPrint($Z,$Ph,$x);adminer()->selectOrderPrint($ig,$jg,$x);adminer()->selectLimitPrint($z);adminer()->selectLengthPrint($Si);adminer()->selectActionPrint($x);echo"
    \n";$D=$_GET["page"];$pd=null;if($D=="last"){$pd=get_val(count_rows($a,$Z,$ve,$xd));if($pd===false&&function_exists(__NAMESPACE__.'\\found_rows'))$pd=found_rows($S,$Z);$D=floor(max(0,intval($pd)-1)/$z);}$Qh=$M;$yd=$xd;if(!$Qh){$Qh[]="*";$Cb=convert_fields($e,$o,$M);if($Cb)$Qh[]=substr($Cb,2);}foreach($M +as$y=>$X){$n=$o[idf_unescape($X)];if($n&&($ya=convert_field($n)))$Qh[$y]="$ya AS $X";}if(!$ve&&$yj){foreach($yj +as$y=>$X){$Qh[]=idf_escape($y);if($yd)$yd[]=idf_escape($y);}}$I=driver()->select($a,$Qh,$Z,$yd,$ig,$z,$D,true);if(!$I)echo"

    ".error()."\n";else{if(JUSH=="mssql"&&$D)$I->seek($z*$D);$wc=array();echo"

    \n";$L=array();while($K=$I->fetch_assoc()){if($D&&JUSH=="oracle")unset($K["RNUM"]);$L[]=$K;}if($_GET["page"]!="last"&&$z&&$xd&&$ve&&JUSH=="sql")$pd=get_val(" SELECT FOUND_ROWS()");if(!$L)echo"

    ".lang(14)."\n";else{$Ga=adminer()->backwardKeys($a,$Di);echo"

    ","",script("mixin(qs('#table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true), onkeydown: editingKeydown});"),"".(!$xd&&$M?"":"\n";if(is_ajax())ob_end_clean();foreach(adminer()->rowDescriptions($L,$md)as$Cf=>$K){$vj=unique_array($L[$Cf],$x);if(!$vj){$vj=array();reset($M);foreach($L[$Cf]as$y=>$X){if(!preg_match('~^(COUNT|AVG|GROUP_CONCAT|MAX|MIN|SUM)\(~',current($M)))$vj[$y]=$X;next($M);}}$wj="";foreach($vj +as$y=>$X){$n=(array)$o[$y];if((JUSH=="sql"||JUSH=="pgsql")&&preg_match('~char|text|enum|set~',$n["type"])&&strlen($X)>64){$y=(strpos($y,'(')?$y:idf_escape($y));$y="MD5(".(JUSH!='sql'||preg_match("~^utf8~",$n["collation"])?$y:"CONVERT($y USING ".charset(connection()).")").")";$X=md5($X);}$wj +.="&".($X!==null?urlencode("where[".bracket_escape($y)."]")."=".urlencode($X===false?"f":$X):"null%5B%5D=".urlencode($y));}echo"".(!$xd&&$M?"":"\n";}if(is_ajax())exit;echo"
    ".script("qs('#all-page').onclick = partial(formCheck, /check/);","")." ".lang(270)."");$Ef=array();$sd=array();reset($M);$mh=1;foreach($L[0]as$y=>$X){if(!isset($yj[$y])){$X=idx($_GET["columns"],key($M))?:array();$n=$o[$M?($X?$X["col"]:current($M)):$y];$B=($n?adminer()->fieldName($n,$mh):($X["fun"]?"*":h($y)));if($B!=""){$mh++;$Ef[$y]=$B;$d=idf_escape($y);$Od=remove_from_uri('(order|desc)[^=]*|page').'&order%5B0%5D='.urlencode($y);$ac="&desc%5B0%5D=1";echo"".script("mixin(qsl('th'), {onmouseover: partial(columnMouse), onmouseout: partial(columnMouse, ' hidden')});","");$rd=apply_sql_function($X["fun"],$B);$ii=isset($n["privileges"]["order"])||$rd!=$B;echo($ii?"$rd":$rd);$pf=($ii?"":'');if(!$X["fun"]&&isset($n["privileges"]["where"])){$pf +.=' =';$pf +.=script("qsl('a').onclick = partial(selectSearch, '".js_escape($y)."');");}echo($pf?"":"");}$sd[$y]=$X["fun"];next($M);}}$Se=array();if($_GET["modify"]){foreach($L +as$K){foreach($K +as$y=>$X)$Se[$y]=max($Se[$y],min(40,strlen(utf8_decode($X))));}}echo($Ga?"".lang(271):"")."
    ".checkbox("check[]",substr($wj,1),in_array(substr($wj,1),(array)$_POST["check"])).($ve||information_schema(DB)?"":" ".lang(272).""));reset($M);foreach($K +as$y=>$X){if(isset($Ef[$y])){$d=current($M);$n=(array)$o[$y];if($X!=""&&(!isset($wc[$y])||$wc[$y]!=""))$wc[$y]=(is_mail($X)?$Ef[$y]:"");$_="";if(is_blob($n)&&$X!="")$_=ME.'download='.urlencode($a).'&field='.urlencode($y).$wj;if(!$_&&$X!==null){foreach((array)$md[$y]as$q){if(count($md[$y])==1||end($q["source"])==$y){$_="";foreach($q["source"]as$t=>$ji)$_ +.=where_link($t,$q["target"][$t],$L[$Cf][$ji]);$_=($q["db"]!=""?preg_replace('~([?&]db=)[^&]+~','\1'.urlencode($q["db"]),ME):ME).'select='.urlencode($q["table"]).$_;if($q["ns"])$_=preg_replace('~([?&]ns=)[^&]+~','\1'.urlencode($q["ns"]),$_);if(count($q["source"])==1)break;}}}if($d=="COUNT(*)"){$_=ME."select=".urlencode($a);$t=0;foreach((array)$_GET["where"]as$W){if(!array_key_exists($W["col"],$vj))$_ +.=where_link($t++,$W["col"],$W["val"],$W["op"]);}foreach($vj +as$Ce=>$W)$_ +.=where_link($t++,$Ce,$W);}$Pd=select_value($X,$_,$n,$Si);$u=h("val[$wj][".bracket_escape($y)."]");$Wg=idx(idx($_POST["val"],$wj),bracket_escape($y));$rc=!is_array($K[$y])&&is_utf8($Pd)&&$L[$Cf][$y]==$K[$y]&&!$sd[$y]&&!$n["generated"];$U=(preg_match('~^(AVG|MIN|MAX)\((.+)\)~',$d,$A)?$o[idf_unescape($A[2])]["type"]:$n["type"]);$Qi=preg_match('~text|json|lob~',$U);$we=preg_match(number_type(),$U)||preg_match('~^(CHAR_LENGTH|ROUND|FLOOR|CEIL|TIME_TO_SEC|COUNT|SUM)\(~',$d);echo"".($Qi?"":"");}else{$Ye=strpos($Pd,"");echo" data-text='".($Ye?2:($Qi?1:0))."'".($rc?"":" data-warning='".h(lang(273))."'").">$Pd";}}next($M);}if($Ga)echo"";adminer()->backwardKeysPrint($Ga,$L[$Cf]);echo"
    \n","
    \n";}if(!is_ajax()){if($L||$D){$Jc=true;if($_GET["page"]!="last"){if(!$z||(count($L)<$z&&($L||!$D)))$pd=($D?$D*$z:0)+count($L);elseif(JUSH!="sql"||!$ve){$pd=($ve?false:found_rows($S,$Z));if(intval($pd)$z||$D));if($yg)echo(($pd===false?count($L)+1:$pd-$D*$z)>$z?'

    '.lang(274).''.script("qsl('a').onclick = partial(selectLoadMore, $z, '".lang(275)."…');",""):''),"\n";echo"

    \n";}if(adminer()->selectImportPrint())echo"

    ","".lang(76)."",script("qsl('a').onclick = partial(toggle, 'import');",""),"";echo +input_token(),"

    \n",(!$xd&&$M?"":script("tableCheck();"));}}}if(is_ajax()){ob_end_clean();exit;}}elseif(isset($_GET["variables"])){$P=isset($_GET["status"]);page_header($P?lang(124):lang(123));$Oj=($P?adminer()->showStatus():adminer()->showVariables());if(!$Oj)echo"

    ".lang(14)."\n";else{echo"\n";foreach($Oj +as$K){echo"";$y=array_shift($K);echo"
    ".h($y)."";foreach($K +as$X)echo"".nl_br(h($X));}echo"
    \n";}}elseif(isset($_GET["script"])){header("Content-Type: text/javascript; charset=utf-8");if($_GET["script"]=="db"){$_i=array("Data_length"=>0,"Index_length"=>0,"Data_free"=>0);foreach(table_status()as$B=>$S){json_row("Comment-$B",h($S["Comment"]));if(!is_view($S)||preg_match('~materialized~i',$S["Engine"])){foreach(array("Engine","Collation")as$y)json_row("$y-$B",h($S[$y]));foreach($_i+array("Auto_increment"=>0,"Rows"=>0)as$y=>$X){if($S[$y]!=""){$X=format_number($S[$y]);if($X>=0)json_row("$y-$B",($y=="Rows"&&$X&&$S["Engine"]==(JUSH=="pgsql"?"table":"InnoDB")?"~ $X":$X));if(isset($_i[$y]))$_i[$y]+=($S["Engine"]!="InnoDB"||$y!="Data_free"?$S[$y]:0);}elseif(array_key_exists($y,$S))json_row("$y-$B","?");}}}foreach($_i +as$y=>$X)json_row("sum-$y",format_number($X));json_row("");}elseif($_GET["script"]=="kill")connection()->query("KILL ".number($_POST["kill"]));else{foreach(count_tables(adminer()->databases())as$k=>$X){json_row("tables-$k",$X);json_row("size-$k",db_size($k));}json_row("");}exit;}else{$Ki=array_merge((array)$_POST["tables"],(array)$_POST["views"]);if($Ki&&!$m&&!$_POST["search"]){$I=true;$qf="";if(JUSH=="sql"&&$_POST["tables"]&&count($_POST["tables"])>1&&($_POST["drop"]||$_POST["truncate"]||$_POST["copy"]))queries("SET foreign_key_checks = 0");if($_POST["truncate"]){if($_POST["tables"])$I=truncate_tables($_POST["tables"]);$qf=lang(279);}elseif($_POST["move"]){$I=move_tables((array)$_POST["tables"],(array)$_POST["views"],$_POST["target"]);$qf=lang(280);}elseif($_POST["copy"]){$I=copy_tables((array)$_POST["tables"],(array)$_POST["views"],$_POST["target"]);$qf=lang(281);}elseif($_POST["drop"]){if($_POST["views"])$I=drop_views($_POST["views"]);if($I&&$_POST["tables"])$I=drop_tables($_POST["tables"]);$qf=lang(282);}elseif(JUSH=="sqlite"&&$_POST["check"]){foreach((array)$_POST["tables"]as$R){foreach(get_rows("PRAGMA integrity_check(".q($R).")")as$K)$qf +.="".h($R).": ".h($K["integrity_check"])."
    ";}}elseif(JUSH!="sql"){$I=(JUSH=="sqlite"?queries("VACUUM"):apply_queries("VACUUM".($_POST["optimize"]?"":" ANALYZE"),$_POST["tables"]));$qf=lang(283);}elseif(!$_POST["tables"])$qf=lang(11);elseif($I=queries(($_POST["optimize"]?"OPTIMIZE":($_POST["check"]?"CHECK":($_POST["repair"]?"REPAIR":"ANALYZE")))." TABLE ".implode(", ",array_map('Adminer\idf_escape',$_POST["tables"])))){while($K=$I->fetch_assoc())$qf +.="".h($K["Table"]).": ".h($K["Msg_text"])."
    ";}queries_redirect(substr(ME,0,-1),$qf,$I);}page_header(($_GET["ns"]==""?lang(38).": ".h(DB):lang(80).": ".h($_GET["ns"])),$m,true);if(adminer()->homepage()){if($_GET["ns"]!==""){echo"

    ".lang(284)."

    \n";$Ji=tables_list();if(!$Ji)echo"

    ".lang(11)."\n";else{echo"

    \n";if(support("table")){echo"
    ".lang(285)."
    ",html_select("op",adminer()->operators(),idx($_POST,"op",JUSH=="elastic"?"should":"LIKE %%"))," ",script("qsl('input').onkeydown = partialArg(bodyKeydown, 'search');","")," \n","
    \n";if($_POST["search"]&&$_POST["query"]!=""){$_GET["where"][0]["op"]=$_POST["op"];search_tables();}}echo"
    \n","\n",script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});"),'','\n";$T=0;foreach($Ji +as$B=>$U){$Rj=($U!==null&&!preg_match('~table|sequence~i',$U));$u=h("Table-".$B);echo'
    '.script("qs('#check-all').onclick = partial(formCheck, /^(tables|views)\[/);",""),''.lang(139);$e=array("Engine"=>array(lang(286).doc_link(array('sql'=>'storage-engines.html'))));if(collations())$e["Collation"]=array(lang(128).doc_link(array('sql'=>'charset-charsets.html','mariadb'=>'supported-character-sets-and-collations/')));if(function_exists('Adminer\alter_table'))$e["Data_length"]=array(lang(287).doc_link(array('sql'=>'show-table-status.html','pgsql'=>'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT','oracle'=>'REFRN20286')),"create",lang(45));if(support('indexes'))$e["Index_length"]=array(lang(288).doc_link(array('sql'=>'show-table-status.html','pgsql'=>'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT')),"indexes",lang(142));$e["Data_free"]=array(lang(289).doc_link(array('sql'=>'show-table-status.html')),"edit",lang(46));if(function_exists('Adminer\alter_table'))$e["Auto_increment"]=array(lang(52).doc_link(array('sql'=>'example-auto-increment.html','mariadb'=>'auto_increment/')),"auto_increment=1&create",lang(45));$e["Rows"]=array(lang(290).doc_link(array('sql'=>'show-table-status.html','pgsql'=>'catalog-pg-class.html#CATALOG-PG-CLASS','oracle'=>'REFRN20286')),"select",lang(42));if(support("comment"))$e["Comment"]=array(lang(51).doc_link(array('sql'=>'show-table-status.html','pgsql'=>'functions-info.html#FUNCTIONS-INFO-COMMENT-TABLE')));foreach($e +as$d)echo"$d[0]";echo"
    '.checkbox(($Rj?"views[]":"tables[]"),$B,in_array("$B",$Ki,true),"","","",$u),''.(support("table")||support("indexes")?"".h($B).'':h($B));if($Rj&&!preg_match('~materialized~i',$U)){$Wi=lang(138);echo''.(support("view")?"$Wi":$Wi),'?';}else{foreach($e +as$y=>$d){$u=" id='$y-".h($B)."'";echo($d[1]?"?":"");}$T++;}echo"\n";}echo"
    ".lang(263,count($Ji)),"".h(JUSH=="sql"?get_val("SELECT @@default_storage_engine"):""),"".h(db_collation(DB,collations()));foreach(array("Data_length","Index_length","Data_free")as$y)echo($e[$y]?"":"");echo"\n","
    \n",script("ajaxSetHtml('".js_escape(ME)."script=db');"),"
    \n";if(!information_schema(DB)){$Lj=" ".on_help("'VACUUM'");$eg=" ".on_help(JUSH=="sql"?"'OPTIMIZE TABLE'":"'VACUUM OPTIMIZE'");$bh=(JUSH=="sqlite"?$Lj." ".on_help("'PRAGMA integrity_check'"):(JUSH=="pgsql"?$Lj.$eg:(JUSH=="sql"?" ".on_help("'ANALYZE TABLE'").$eg." ".on_help("'CHECK TABLE'")." ".on_help("'REPAIR TABLE'"):""))).(function_exists('Adminer\truncate_tables')?" ".on_help(JUSH=="sqlite"?"'DELETE'":"'TRUNCATE".(JUSH=="pgsql"?"'":" TABLE'")).confirm():"").(function_exists('Adminer\drop_tables')?"".on_help("'DROP TABLE'").confirm():"");echo($bh?"\n";}echo"
    \n",script("tableCheck();");}echo(function_exists('Adminer\alter_table')?"

    ".lang(73)."

    \n";$Fh=routines();if($Fh){echo"\n",'\n";foreach($Fh +as$K){$B=($K["SPECIFIC_NAME"]==$K["ROUTINE_NAME"]?"":"&name=".urlencode($K["ROUTINE_NAME"]));echo'','
    '.lang(194).''.lang(50).''.lang(233)."
    '.h($K["ROUTINE_NAME"]).'',''.h($K["ROUTINE_TYPE"]),''.h($K["DTD_IDENTIFIER"]),''.lang(145)."";}echo"
    \n";}echo'

    ".lang(74)."

    \n";$Yh=get_vals("SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = current_schema() ORDER BY sequence_name");if($Yh){echo"\n","\n";foreach($Yh +as$X)echo"
    ".lang(194)."
    ".h($X)."\n";echo"
    \n";}echo"

    ".lang(6)."

    \n";$Ij=types();if($Ij){echo"\n","\n";foreach($Ij +as$X)echo"
    ".lang(194)."
    ".h($X)."\n";echo"
    \n";}echo"

    ".lang(75)."

    \n";$L=get_rows("SHOW EVENTS");if($L){echo"\n","\n";foreach($L +as$K)echo"","
    ".lang(194)."".lang(301)."".lang(222)."".lang(223)."
    ".h($K["Name"]),"".($K["Execute at"]?lang(302)."".$K["Execute at"]:lang(224)." ".$K["Interval value"]." ".$K["Interval field"]."$K[Starts]"),"$K[Ends]",''.lang(145).'';echo"
    \n";$Hc=get_val("SELECT @@event_scheduler");if($Hc&&$Hc!="ON")echo"

    event_scheduler: ".h($Hc)."\n";}echo'

    " . error() . "\n"; } else { $connection2 = connect(); - if (is_object($connection2)) { + if ($connection2) { $connection2->select_db(DB); } - + do { - $result = $connection->store_result(); + $result = connection()->store_result(); if (is_object($result)) { - select($result, $connection2); + print_select_result($result, $connection2); } else { echo "

    " . lang('Routine has been called, %d row(s) affected.', $affected) . " " . @date("H:i:s") . "\n" // @ - time zone may be not set ; } - } while ($connection->next_result()); - + } while (connection()->next_result()); + if ($out) { - select($connection->query("SELECT " . implode(", ", $out))); + print_select_result(connection()->query("SELECT " . implode(", ", $out))); } } } @@ -64,21 +71,18 @@

    \n"; + echo "\n"; foreach ($in as $key) { $field = $routine["fields"][$key]; $name = $field["field"]; - echo "
    " . $adminer->fieldName($field); - $value = $_POST["fields"][$name]; + echo "
    " . adminer()->fieldName($field); + $value = idx($_POST["fields"], $name); if ($value != "") { - if ($field["type"] == "enum") { - $value = +$value; - } if ($field["type"] == "set") { - $value = array_sum($value); + $value = implode(",", $value); } } - input($field, $value, (string) $_POST["function"][$name]); // param name can be empty + input($field, $value, idx($_POST["function"], $name, "")); // param name can be empty echo "\n"; } echo "
    \n"; @@ -86,5 +90,31 @@ ?>

    - +

    + +
    +', preg_replace('~\|~', '', preg_replace('~\|$~m', "", rtrim($s))));
    +}
    +
    +$table = '(\+--[-+]+\+\n)';
    +$row = '(\| .* \|\n)';
    +echo preg_replace_callback(
    +	"~^$table?$row$table?($row*)$table?~m",
    +	function ($match) {
    +		$first_row = pre_tr($match[2]);
    +		return "\n" . ($match[1] ? "$first_row\n" : $first_row) . pre_tr($match[4]) . "\n
    "; + }, + preg_replace( + '~(\n( -|mysql)> )(.+)~', + "\\1\\3", + preg_replace('~(.+)\n---+\n~', "\\1\n", h($routine['comment'])) + ) +); +?> +
    diff --git a/adminer/check.inc.php b/adminer/check.inc.php new file mode 100644 index 000000000..4c191430c --- /dev/null +++ b/adminer/check.inc.php @@ -0,0 +1,51 @@ + $TABLE)); + +if (!$row) { + $checks = driver()->checkConstraints($TABLE); + $row = array("name" => $name, "clause" => $checks[$name]); +} +?> + +
    +

    '; +} +echo doc_link(array( + 'sql' => "create-table-check-constraints.html", + 'mariadb' => "constraint/", + 'pgsql' => "ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS", + 'mssql' => "relational-databases/tables/create-check-constraints", + 'sqlite' => "lang_createtable.html#check_constraints", +), "?"); +?> +

    +

    + + + + +

    diff --git a/adminer/create.inc.php b/adminer/create.inc.php index 4e57f78db..4d7a9c809 100644 --- a/adminer/create.inc.php +++ b/adminer/create.inc.php @@ -1,9 +1,9 @@ partitionBy; +$partitions_info = ($partition_by ? driver()->partitionsInfo($TABLE) : array()); $referencable_primary = referencable_primary($TABLE); $foreign_keys = array(); @@ -15,8 +15,8 @@ $table_status = array(); if ($TABLE != "") { $orig_fields = fields($TABLE); - $table_status = table_status($TABLE); - if (!$table_status) { + $table_status = table_status1($TABLE); + if (count($table_status) < 2) { // there's only the Name field $error = lang('No tables.'); } } @@ -28,7 +28,7 @@ } if ($_POST) { - set_adminer_settings(array("comments" => $_POST["comments"], "defaults" => $_POST["defaults"])); + save_settings(array("comments" => $_POST["comments"], "defaults" => $_POST["defaults"])); } if ($_POST && !process_fields($row["fields"]) && !$error) { @@ -46,22 +46,19 @@ $foreign_key = $foreign_keys[$field["type"]]; $type_field = ($foreign_key !== null ? $referencable_primary[$foreign_key] : $field); //! can collide with user defined type if ($field["field"] != "") { - if (!$field["has_default"]) { + if (!$field["generated"]) { $field["default"] = null; } - if ($key == $row["auto_increment_col"]) { - $field["auto_increment"] = true; - } $process_field = process_field($field, $type_field); $all_fields[] = array($field["orig"], $process_field, $after); - if (!$orig_field || $process_field != process_field($orig_field, $orig_field)) { + if (!$orig_field || $process_field !== process_field($orig_field, $orig_field)) { $fields[] = array($field["orig"], $process_field, $after); if ($field["orig"] != "" || $after) { $use_all_fields = true; } } if ($foreign_key !== null) { - $foreign[idf_escape($field["field"])] = ($TABLE != "" && $jush != "sqlite" ? "ADD" : " ") . format_foreign_key(array( + $foreign[idf_escape($field["field"])] = ($TABLE != "" && JUSH != "sqlite" ? "ADD" : " ") . format_foreign_key(array( 'table' => $foreign_keys[$field["type"]], 'source' => array($field["field"]), 'target' => array($type_field["field"]), @@ -81,21 +78,26 @@ } } - $partitioning = ""; - if ($partition_by[$row["partition_by"]]) { - $partitions = array(); - if ($row["partition_by"] == 'RANGE' || $row["partition_by"] == 'LIST') { - foreach (array_filter($row["partition_names"]) as $key => $val) { - $value = $row["partition_values"][$key]; - $partitions[] = "\n PARTITION " . idf_escape($val) . " VALUES " . ($row["partition_by"] == 'RANGE' ? "LESS THAN" : "IN") . ($value != "" ? " ($value)" : " MAXVALUE"); //! SQL injection + $partitioning = array(); + if (in_array($row["partition_by"], $partition_by)) { + foreach ($row as $key => $val) { + if (preg_match('~^partition~', $key)) { + $partitioning[$key] = $val; } } - $partitioning .= "\nPARTITION BY $row[partition_by]($row[partition])" . ($partitions // $row["partition"] can be expression, not only column - ? " (" . implode(",", $partitions) . "\n)" - : ($row["partitions"] ? " PARTITIONS " . (+$row["partitions"]) : "") - ); - } elseif (support("partitioning") && preg_match("~partitioned~", $table_status["Create_options"])) { - $partitioning .= "\nREMOVE PARTITIONING"; + foreach ($partitioning["partition_names"] as $key => $name) { + if ($name == "") { + unset($partitioning["partition_names"][$key]); + unset($partitioning["partition_values"][$key]); + } + } + $partitioning["partition_names"] = array_values($partitioning["partition_names"]); + $partitioning["partition_values"] = array_values($partitioning["partition_values"]); + if ($partitioning == $partitions_info) { + $partitioning = array(); + } + } elseif (preg_match("~partitioned~", $table_status["Create_options"])) { + $partitioning = null; } $message = lang('Table has been altered.'); @@ -108,7 +110,7 @@ queries_redirect(ME . (support("table") ? "table=" : "select=") . urlencode($name), $message, alter_table( $TABLE, $name, - ($jush == "sqlite" && ($use_all_fields || $foreign) ? $all_fields : $fields), + (JUSH == "sqlite" && ($use_all_fields || $foreign) ? $all_fields : $fields), $foreign, ($row["Comment"] != $table_status["Comment"] ? $row["Comment"] : null), ($row["Engine"] && $row["Engine"] != $table_status["Engine"] ? $row["Engine"] : ""), @@ -122,6 +124,7 @@ page_header(($TABLE != "" ? lang('Alter table') : lang('Create table')), $error, array("table" => $TABLE), h($TABLE)); if (!$_POST) { + $types = driver()->types(); $row = array( "Engine" => $_COOKIE["adminer_engine"], "fields" => array(array("field" => "", "type" => (isset($types["int"]) ? "int" : (isset($types["integer"]) ? "integer" : "")), "on_update" => "")), @@ -136,24 +139,23 @@ $row["Auto_increment"] = ""; } foreach ($orig_fields as $field) { - $field["has_default"] = isset($field["default"]); + $field["generated"] = $field["generated"] ?: (isset($field["default"]) ? "DEFAULT" : ""); $row["fields"][] = $field; } - if (support("partitioning")) { - $from = "FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = " . q(DB) . " AND TABLE_NAME = " . q($TABLE); - $result = $connection->query("SELECT PARTITION_METHOD, PARTITION_ORDINAL_POSITION, PARTITION_EXPRESSION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1"); - list($row["partition_by"], $row["partitions"], $row["partition"]) = $result->fetch_row(); - $partitions = get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $from AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION"); - $partitions[""] = ""; - $row["partition_names"] = array_keys($partitions); - $row["partition_values"] = array_values($partitions); + if ($partition_by) { + $row += $partitions_info; + $row["partition_names"][] = ""; + $row["partition_values"][] = ""; } } } $collations = collations(); -$engines = engines(); +if (is_array(reset($collations))) { + $collations = call_user_func_array('array_merge', array_values($collations)); +} +$engines = driver()->engines(); // case of engine may differ foreach ($engines as $engine) { if (!strcasecmp($engine, $row["Engine"])) { @@ -165,59 +167,60 @@

    - -: " autocapitalize="off"> - -" . optionlist(array("" => "(" . lang('engine') . ")") + $engines, $row["Engine"]) . "" . on_help("getTarget(event).value", 1) . script("qsl('select').onchange = helpClose;") : ""); ?> - "(" . lang('collation') . ")") + $collations, $row["Collation"]) : ""); ?> - - - - -

    - -
    - -
    -

    -: "> - -' - : '') -; ?> +if (support("columns") || $TABLE == "") { + echo lang('Table name') . ": \n"; + echo ($engines ? html_select("Engine", array("" => "(" . lang('engine') . ")") + $engines, $row["Engine"]) . on_help("event.target.value", 1) . script("qsl('select').onchange = helpClose;") . "\n" : ""); + if ($collations) { + echo "" . optionlist($collations) . "\n"; + echo (preg_match("~sqlite|mssql~", JUSH) ? "" : "\n"); + } + echo "\n"; +} + +if (support("columns")) { + echo "

    \n"; + echo "\n"; + edit_fields($row["fields"], $collations, "TABLE", $foreign_keys); + echo "
    \n"; + echo script("editFields();"); + echo "
    \n

    \n"; + echo lang('Auto Increment') . ": \n"; + echo checkbox("defaults", 1, ($_POST ? $_POST["defaults"] : get_setting("defaults")), lang('Default values'), "columnShow(this.checked, 5)", "jsonly"); + $comments = ($_POST ? $_POST["comments"] : get_setting("comments")); + echo (support("comment") + ? checkbox("comments", 1, $comments, lang('Comment'), "editingCommentsClick(this, true);", "jsonly") + . ' ' . (preg_match('~\n~', $row["Comment"]) + ? "" + : '' + ) + : '') + ; + ?>

    - + + + -

    -" . optionlist(array("" => "") + $partition_by, $row["partition_by"]) . "" . on_help("getTarget(event).value.replace(/./, 'PARTITION BY \$&')", 1) . script("qsl('select').onchange = partitionByChange;"); ?> -(">) -: " value=""> -> - - $val) { - echo ''; - echo ' - -" . html_select("partition_by", array_merge(array(""), $partition_by), $row["partition_by"]) . on_help("event.target.value.replace(/./, 'PARTITION BY \$&')", 1) . script("qsl('select').onchange = partitionByChange;"); + echo "()\n"; + echo lang('Partitions') . ": \n"; + echo "\n"; + echo "\n"; + foreach ($row["partition_names"] as $key => $val) { + echo ''; + echo '\n\n"; } +echo input_token(); ?> -

    diff --git a/adminer/database.inc.php b/adminer/database.inc.php index bc5e4dbf3..828f9d176 100644 --- a/adminer/database.inc.php +++ b/adminer/database.inc.php @@ -1,7 +1,9 @@

    ' . h($name) . '
    ' - : '' +echo ($_POST["add"] || strpos($name, "\n") + ? '
    ' + : '' ) . "\n" . ($collations ? html_select("collation", array("" => "(" . lang('collation') . ")") + $collations, $row["collation"]) . doc_link(array( 'sql' => "charset-charsets.html", 'mariadb' => "supported-character-sets-and-collations/", - 'mssql' => "ms187963.aspx", + 'mssql' => "relational-databases/system-functions/sys-fn-helpcollations-transact-sql", )) : ""); -echo script("focus(qs('#name'));"); ?> " . confirm(lang('Drop %s?', DB)) . "\n"; -} elseif (!$_POST["add_x"] && $_GET["db"] == "") { - echo "\n"; +} elseif (!$_POST["add"] && $_GET["db"] == "") { + echo icon("plus", "add[0]", "+", lang('Add next')) . "\n"; } +echo input_token(); ?> - diff --git a/adminer/db.inc.php b/adminer/db.inc.php index 6c879fcee..2cd351acf 100644 --- a/adminer/db.inc.php +++ b/adminer/db.inc.php @@ -1,10 +1,12 @@ 1 && ($_POST["drop"] || $_POST["truncate"] || $_POST["copy"])) { + if (JUSH == "sql" && $_POST["tables"] && count($_POST["tables"]) > 1 && ($_POST["drop"] || $_POST["truncate"] || $_POST["copy"])) { queries("SET foreign_key_checks = 0"); // allows to truncate or drop several tables at once } @@ -27,15 +29,21 @@ $result = drop_tables($_POST["tables"]); } $message = lang('Tables have been dropped.'); - } elseif ($jush != "sql") { - $result = ($jush == "sqlite" + } elseif (JUSH == "sqlite" && $_POST["check"]) { + foreach ((array) $_POST["tables"] as $table) { + foreach (get_rows("PRAGMA integrity_check(" . q($table) . ")") as $row) { + $message .= "" . h($table) . ": " . h($row["integrity_check"]) . "
    "; + } + } + } elseif (JUSH != "sql") { + $result = (JUSH == "sqlite" ? queries("VACUUM") : apply_queries("VACUUM" . ($_POST["optimize"] ? "" : " ANALYZE"), $_POST["tables"]) ); $message = lang('Tables have been optimized.'); } elseif (!$_POST["tables"]) { $message = lang('No tables.'); - } elseif ($result = queries(($_POST["optimize"] ? "OPTIMIZE" : ($_POST["check"] ? "CHECK" : ($_POST["repair"] ? "REPAIR" : "ANALYZE"))) . " TABLE " . implode(", ", array_map('idf_escape', $_POST["tables"])))) { + } elseif ($result = queries(($_POST["optimize"] ? "OPTIMIZE" : ($_POST["check"] ? "CHECK" : ($_POST["repair"] ? "REPAIR" : "ANALYZE"))) . " TABLE " . implode(", ", array_map('Adminer\idf_escape', $_POST["tables"])))) { while ($row = $result->fetch_assoc()) { $message .= "" . h($row["Table"]) . ": " . h($row["Msg_text"]) . "
    "; } @@ -46,7 +54,7 @@ page_header(($_GET["ns"] == "" ? lang('Database') . ": " . h(DB) : lang('Schema') . ": " . h($_GET["ns"])), $error, true); -if ($adminer->homepage()) { +if (adminer()->homepage()) { if ($_GET["ns"] !== "") { echo "

    " . lang('Tables and views') . "

    \n"; $tables_list = tables_list(); @@ -56,115 +64,128 @@ echo "
    \n"; if (support("table")) { echo "
    " . lang('Search data in tables') . "
    "; - echo ""; + echo html_select("op", adminer()->operators(), idx($_POST, "op", JUSH == "elastic" ? "should" : "LIKE %%")); + echo " "; echo script("qsl('input').onkeydown = partialArg(bodyKeydown, 'search');", ""); echo " \n"; echo "
    \n"; if ($_POST["search"] && $_POST["query"] != "") { - $_GET["where"][0]["op"] = "LIKE %%"; + $_GET["where"][0]["op"] = $_POST["op"]; search_tables(); } } echo "
    \n"; - echo "\n"; + echo "
    \n"; echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});"); echo ''; echo '\n"; $tables = 0; foreach ($tables_list as $name => $type) { $view = ($type !== null && !preg_match('~table|sequence~i', $type)); $id = h("Table-" . $name); - echo '
    ' . script("qs('#check-all').onclick = partial(formCheck, /^(tables|views)\[/);", ""); echo '' . lang('Table'); - echo '' . lang('Engine') . doc_link(array('sql' => 'storage-engines.html')); - echo '' . lang('Collation') . doc_link(array('sql' => 'charset-charsets.html', 'mariadb' => 'supported-character-sets-and-collations/')); - echo '' . lang('Data Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT', 'oracle' => 'REFRN20286')); - echo '' . lang('Index Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT')); - echo '' . lang('Data Free') . doc_link(array('sql' => 'show-table-status.html')); - echo '' . lang('Auto Increment') . doc_link(array('sql' => 'example-auto-increment.html', 'mariadb' => 'auto_increment/')); - echo '' . lang('Rows') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'catalog-pg-class.html#CATALOG-PG-CLASS', 'oracle' => 'REFRN20286')); - echo (support("comment") ? '' . lang('Comment') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-info.html#FUNCTIONS-INFO-COMMENT-TABLE')) : ''); + $columns = array("Engine" => array(lang('Engine') . doc_link(array('sql' => 'storage-engines.html')))); + if (collations()) { + $columns["Collation"] = array(lang('Collation') . doc_link(array('sql' => 'charset-charsets.html', 'mariadb' => 'supported-character-sets-and-collations/'))); + } + if (function_exists('Adminer\alter_table')) { + $columns["Data_length"] = array(lang('Data Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT', 'oracle' => 'REFRN20286')), "create", lang('Alter table')); + } + if (support('indexes')) { + $columns["Index_length"] = array(lang('Index Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT')), "indexes", lang('Alter indexes')); + } + $columns["Data_free"] = array(lang('Data Free') . doc_link(array('sql' => 'show-table-status.html')), "edit", lang('New item')); + if (function_exists('Adminer\alter_table')) { + $columns["Auto_increment"] = array(lang('Auto Increment') . doc_link(array('sql' => 'example-auto-increment.html', 'mariadb' => 'auto_increment/')), "auto_increment=1&create", lang('Alter table')); + } + $columns["Rows"] = array(lang('Rows') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'catalog-pg-class.html#CATALOG-PG-CLASS', 'oracle' => 'REFRN20286')), "select", lang('Select data')); + if (support("comment")) { + $columns["Comment"] = array(lang('Comment') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-info.html#FUNCTIONS-INFO-COMMENT-TABLE'))); + } + foreach ($columns as $column) { + echo "$column[0]"; + } echo "
    ' . checkbox(($view ? "views[]" : "tables[]"), $name, in_array($name, $tables_views, true), "", "", "", $id); + echo '
    ' . checkbox(($view ? "views[]" : "tables[]"), $name, in_array("$name", $tables_views, true), "", "", "", $id); // "$name" to check numeric table names echo '' . (support("table") || support("indexes") ? "" . h($name) . '' : h($name)); - if ($view) { - echo '' . (preg_match('~materialized~i', $type) ? lang('Materialized view') : lang('View')) . ''; + if ($view && !preg_match('~materialized~i', $type)) { + $title = lang('View'); + echo '' . (support("view") ? "$title" : $title); echo '?'; } else { - foreach (array( - "Engine" => array(), - "Collation" => array(), - "Data_length" => array("create", lang('Alter table')), - "Index_length" => array("indexes", lang('Alter indexes')), - "Data_free" => array("edit", lang('New item')), - "Auto_increment" => array("auto_increment=1&create", lang('Alter table')), - "Rows" => array("select", lang('Select data')), - ) as $key => $link) { + foreach ($columns as $key => $column) { $id = " id='$key-" . h($name) . "'"; - echo ($link ? "" . (support("table") || $key == "Rows" || (support("indexes") && $key != "Data_length") - ? "?" - : "?" - ) : ""); + echo ($column[1] + ? "?" + : "" + ); } $tables++; } - echo (support("comment") ? "" : ""); + echo "\n"; } echo "
    " . lang('%d in total', count($tables_list)); - echo "" . h($jush == "sql" ? $connection->result("SELECT @@default_storage_engine") : ""); + echo "" . h(JUSH == "sql" ? get_val("SELECT @@default_storage_engine") : ""); echo "" . h(db_collation(DB, collations())); foreach (array("Data_length", "Index_length", "Data_free") as $key) { - echo ""; + echo ($columns[$key] ? "" : ""); } + echo "\n"; echo "
    \n"; + echo script("ajaxSetHtml('" . js_escape(ME) . "script=db');"); echo "
    \n"; if (!information_schema(DB)) { - echo "\n"; } echo "
    \n"; echo script("tableCheck();"); } - echo '

    " . lang('Routines') . "

    \n"; $routines = routines(); if ($routines) { - echo "\n"; + echo "
    \n"; echo '\n"; - odd(''); foreach ($routines as $row) { $name = ($row["SPECIFIC_NAME"] == $row["ROUTINE_NAME"] ? "" : "&name=" . urlencode($row["ROUTINE_NAME"])); // not computed on the pages to be able to print the header first - echo ''; + echo ''; echo '
    ' . lang('Name') . '' . lang('Type') . '' . lang('Return type') . "
    ' . h($row["ROUTINE_NAME"]) . ''; echo '' . h($row["ROUTINE_TYPE"]); echo '' . h($row["DTD_IDENTIFIER"]); @@ -182,11 +203,10 @@ echo "

    " . lang('Sequences') . "

    \n"; $sequences = get_vals("SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = current_schema() ORDER BY sequence_name"); if ($sequences) { - echo "\n"; + echo "
    \n"; echo "\n"; - odd(''); foreach ($sequences as $val) { - echo "
    " . lang('Name') . "
    " . h($val) . "\n"; + echo "
    " . h($val) . "\n"; } echo "
    \n"; } @@ -197,11 +217,10 @@ echo "

    " . lang('User types') . "

    \n"; $user_types = types(); if ($user_types) { - echo "\n"; + echo "
    \n"; echo "\n"; - odd(''); foreach ($user_types as $val) { - echo "
    " . lang('Name') . "
    " . h($val) . "\n"; + echo "
    " . h($val) . "\n"; } echo "
    \n"; } @@ -212,7 +231,7 @@ echo "

    " . lang('Events') . "

    \n"; $rows = get_rows("SHOW EVENTS"); if ($rows) { - echo "\n"; + echo "
    \n"; echo "\n"; foreach ($rows as $row) { echo ""; @@ -222,16 +241,12 @@ echo '
    " . lang('Name') . "" . lang('Schedule') . "" . lang('Start') . "" . lang('End') . "
    ' . lang('Alter') . ''; } echo "
    \n"; - $event_scheduler = $connection->result("SELECT @@event_scheduler"); + $event_scheduler = get_val("SELECT @@event_scheduler"); if ($event_scheduler && $event_scheduler != "ON") { echo "

    event_scheduler: " . h($event_scheduler) . "\n"; } } echo '

    - +
    "text", "format" => "sql", "db_style" => (DB != "" ? "" : "CREATE"), "table_style" => "DROP+CREATE", "data_style" => "INSERT"); } @@ -154,11 +158,12 @@ $row["triggers"] = $row["table_style"]; } -echo "
    " . lang('Output') . "" . html_select("output", $adminer->dumpOutput(), $row["output"], 0) . "\n"; // 0 - radio +echo "
    " . lang('Output') . "" . html_radios("output", adminer()->dumpOutput(), $row["output"]) . "\n"; -echo "
    " . lang('Format') . "" . html_select("format", $adminer->dumpFormat(), $row["format"], 0) . "\n"; // 0 - radio +echo "
    " . lang('Format') . "" . html_radios("format", adminer()->dumpFormat(), $row["format"]) . "\n"; -echo ($jush == "sqlite" ? "" : "
    " . lang('Database') . "" . html_select('db_style', $db_style, $row["db_style"]) +echo (JUSH == "sqlite" ? "" : "
    " . lang('Database') . "" . html_select('db_style', $db_style, $row["db_style"]) + . (support("type") ? checkbox("types", 1, $row["types"], lang('User types')) : "") . (support("routine") ? checkbox("routines", 1, $row["routines"], lang('Routines')) : "") . (support("event") ? checkbox("events", 1, $row["events"], lang('Events')) : "") ); @@ -172,9 +177,9 @@ ?>

    - + - +
    " . lang('Database') . ""; echo script("qs('#check-databases').onclick = partial(formCheck, /^databases\\[/);", ""); echo "\n"; - $databases = $adminer->databases(); + $databases = adminer()->databases(); if ($databases) { foreach ($databases as $db) { if (!information_schema($db)) { diff --git a/adminer/edit.inc.php b/adminer/edit.inc.php index bdc1fca6f..b47f5e418 100644 --- a/adminer/edit.inc.php +++ b/adminer/edit.inc.php @@ -1,10 +1,15 @@ $field) { - if (!isset($field["privileges"][$update ? "update" : "insert"]) || $adminer->fieldName($field) == "" || $field["generated"]) { + if ((!$update && !isset($field["privileges"]["insert"])) || adminer()->fieldName($field) == "") { unset($fields[$name]); } } @@ -25,7 +30,7 @@ queries_redirect( $location, lang('Item has been deleted.'), - $driver->delete($TABLE, $query_where, !$unique_array) + driver()->delete($TABLE, $query_where, $unique_array ? 0 : 1) ); } else { @@ -44,7 +49,7 @@ queries_redirect( $location, lang('Item has been updated.'), - $driver->update($TABLE, $set, $query_where, !$unique_array) + driver()->update($TABLE, $set, $query_where, $unique_array ? 0 : 1) ); if (is_ajax()) { page_headers(); @@ -52,27 +57,19 @@ exit; } } else { - $result = $driver->insert($TABLE, $set); - $last_id = ($result ? last_id() : 0); + $result = driver()->insert($TABLE, $set); + $last_id = ($result ? last_id($result) : 0); queries_redirect($location, lang('Item%s has been inserted.', ($last_id ? " $last_id" : "")), $result); //! link } } } $row = null; -if ($_POST["save"]) { - $row = (array) $_POST["fields"]; -} elseif ($where) { +if ($where) { $select = array(); foreach ($fields as $name => $field) { if (isset($field["privileges"]["select"])) { - $as = convert_field($field); - if ($_POST["clone"] && $field["auto_increment"]) { - $as = "''"; - } - if ($jush == "sql" && preg_match("~enum|set~", $field["type"])) { - $as = "1*" . idf_escape($name); - } + $as = ($_POST["clone"] && $field["auto_increment"] ? "''" : convert_field($field)); $select[] = ($as ? "$as AS " : "") . idf_escape($name); } } @@ -81,7 +78,7 @@ $select = array("*"); } if ($select) { - $result = $driver->select($TABLE, $select, array($where), $select, array(), (isset($_GET["select"]) ? 2 : 1)); + $result = driver()->select($TABLE, $select, array($where), $select, array(), (isset($_GET["select"]) ? 2 : 1)); if (!$result) { $error = error(); } else { @@ -96,12 +93,12 @@ } } -if (!support("table") && !$fields) { +if (!support("table") && !$fields) { // used by Mongo and SimpleDB if (!$where) { // insert - $result = $driver->select($TABLE, array("*"), $where, array("*")); + $result = driver()->select($TABLE, array("*"), array(), array("*")); $row = ($result ? $result->fetch_assoc() : false); if (!$row) { - $row = array($driver->primary => ""); + $row = array(driver()->primary => ""); } } if ($row) { @@ -109,9 +106,13 @@ if (!$where) { $row[$key] = null; } - $fields[$key] = array("field" => $key, "null" => ($key != $driver->primary), "auto_increment" => ($key == $driver->primary)); + $fields[$key] = array("field" => $key, "null" => ($key != driver()->primary), "auto_increment" => ($key == driver()->primary)); } } } -edit_form($TABLE, $fields, $row, $update); +if ($_POST["save"]) { + $row = (array) $_POST["fields"] + ($row ? $row : array()); +} + +edit_form($TABLE, $fields, $row, $update, $error); diff --git a/adminer/elastic.php b/adminer/elastic.php new file mode 100644 index 000000000..bc724c297 --- /dev/null +++ b/adminer/elastic.php @@ -0,0 +1,13 @@ + "ENABLE", "DISABLED" => "DISABLE", "SLAVESIDE_DISABLED" => "DISABLE ON SLAVE"); @@ -15,14 +17,18 @@ : "AT " . q($row["STARTS"]) ) . " ON COMPLETION" . ($row["ON_COMPLETION"] ? "" : " NOT") . " PRESERVE" ; - - queries_redirect(substr(ME, 0, -1), ($EVENT != "" ? lang('Event has been altered.') : lang('Event has been created.')), queries(($EVENT != "" - ? "ALTER EVENT " . idf_escape($EVENT) . $schedule - . ($EVENT != $row["EVENT_NAME"] ? "\nRENAME TO " . idf_escape($row["EVENT_NAME"]) : "") - : "CREATE EVENT " . idf_escape($row["EVENT_NAME"]) . $schedule - ) . "\n" . $statuses[$row["STATUS"]] . " COMMENT " . q($row["EVENT_COMMENT"]) - . rtrim(" DO\n$row[EVENT_DEFINITION]", ";") . ";" - )); + + queries_redirect( + substr(ME, 0, -1), + ($EVENT != "" ? lang('Event has been altered.') : lang('Event has been created.')), + queries( + ($EVENT != "" + ? "ALTER EVENT " . idf_escape($EVENT) . $schedule . ($EVENT != $row["EVENT_NAME"] ? "\nRENAME TO " . idf_escape($row["EVENT_NAME"]) : "") + : "CREATE EVENT " . idf_escape($row["EVENT_NAME"]) . $schedule + ) . "\n" . $statuses[$row["STATUS"]] . " COMMENT " . q($row["EVENT_COMMENT"]) + . rtrim(" DO\n$row[EVENT_DEFINITION]", ";") . ";" + ) + ); } } @@ -35,7 +41,7 @@ ?> -
    +
    " data-maxlength="64" autocapitalize="off">
    ">
    "> @@ -47,6 +53,8 @@

    - - + + + + diff --git a/adminer/file.inc.php b/adminer/file.inc.php index 8c817697a..ca2aa27aa 100644 --- a/adminer/file.inc.php +++ b/adminer/file.inc.php @@ -1,26 +1,43 @@ ($_POST["drop"] ? "" : " " . format_foreign_key($row))))); + + if (JUSH == "sqlite") { + $result = recreate_table($TABLE, $TABLE, array(), array(), array(" $name" => ($row["drop"] ? "" : " " . format_foreign_key($row)))); } else { $alter = "ALTER TABLE " . table($TABLE); - $drop = "\nDROP " . ($jush == "sql" ? "FOREIGN KEY " : "CONSTRAINT ") . idf_escape($name); - if ($_POST["drop"]) { - query_redirect($alter . $drop, $location, $message); - } else { - query_redirect($alter . ($name != "" ? "$drop," : "") . "\nADD" . format_foreign_key($row), $location, $message); - $error = lang('Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.') . "
    $error"; //! no partitioning + $result = ($name == "" || queries("$alter DROP " . (JUSH == "sql" ? "FOREIGN KEY " : "CONSTRAINT ") . idf_escape($name))); + if (!$row["drop"]) { + $result = queries("$alter ADD" . format_foreign_key($row)); } } + queries_redirect( + ME . "table=" . urlencode($TABLE), + ($row["drop"] ? lang('Foreign key has been dropped.') : ($name != "" ? lang('Foreign key has been altered.') : lang('Foreign key has been created.'))), + $result + ); + if (!$row["drop"]) { + $error = lang('Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.'); //! no partitioning + } } page_header(lang('Foreign key'), $error, array("table" => $TABLE), h($TABLE)); @@ -54,54 +58,64 @@ select_db($row["db"]); + connection()->select_db($row["db"]); } if ($row["ns"] != "") { + $orig_schema = get_schema(); set_schema($row["ns"]); } -$referencable = array_keys(array_filter(table_status('', true), 'fk_support')); +$referencable = array_keys(array_filter(table_status('', true), 'Adminer\fk_support')); $target = array_keys(fields(in_array($row["table"], $referencable) ? $row["table"] : reset($referencable))); $onchange = "this.form['change-js'].value = '1'; this.form.submit();"; -echo "

    " . lang('Target table') . ": " . html_select("table", $referencable, $row["table"], $onchange) . "\n"; -if ($jush == "pgsql") { - echo lang('Schema') . ": " . html_select("ns", $adminer->schemas(), $row["ns"] != "" ? $row["ns"] : $_GET["ns"], $onchange); -} elseif ($jush != "sqlite") { +echo "

    \n"; +if (support("scheme")) { + $schemas = array_filter(adminer()->schemas(), function ($schema) { + return !preg_match('~^information_schema$~i', $schema); + }); + echo ""; + if ($row["ns"] != "") { + set_schema($orig_schema); + } +} elseif (JUSH != "sqlite") { $dbs = array(); - foreach ($adminer->databases() as $db) { + foreach (adminer()->databases() as $db) { if (!information_schema($db)) { $dbs[] = $db; } } - echo lang('DB') . ": " . html_select("db", $dbs, $row["db"] != "" ? $row["db"] : $_GET["db"], $onchange); + echo ""; } +echo input_hidden("change-js"); ?> -

    - +
    $val) { echo ""; - echo "
    " . html_select("source[" . (+$key) . "]", array(-1 => "") + $source, $val, ($j == count($row["source"]) - 1 ? "foreignAddRow.call(this);" : 1), "label-source"); - echo "" . html_select("target[" . (+$key) . "]", $target, $row["target"][$key], 1, "label-target"); + echo "" . html_select("source[" . (+$key) . "]", array(-1 => "") + $source, $val, ($j == count($row["source"]) - 1 ? "foreignAddRow.call(this);" : ""), "label-source"); + echo "" . html_select("target[" . (+$key) . "]", $target, idx($row["target"], $key), "", "label-target"); $j++; } ?>

    -: "") + explode("|", $on_actions), $row["on_delete"]); ?> - : "") + explode("|", $on_actions), $row["on_update"]); ?> + + + "innodb-foreign-key-constraints.html", 'mariadb' => "foreign-keys/", - 'pgsql' => "sql-createtable.html#SQL-CREATETABLE-REFERENCES", - 'mssql' => "ms174979.aspx", - 'oracle' => "https://docs.oracle.com/cd/B19306_01/server.102/b14200/clauses002.htm#sthref2903", + 'pgsql' => "sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES", + 'mssql' => "t-sql/statements/create-table-transact-sql", + 'oracle' => "SQLRF01111", )); ?>

    - - + + + + diff --git a/adminer/include/adminer.inc.php b/adminer/include/adminer.inc.php index 632e29283..933ec267d 100644 --- a/adminer/include/adminer.inc.php +++ b/adminer/include/adminer.inc.php @@ -1,150 +1,167 @@ f() instead of $this->f() to give chance to other plugins */ class Adminer { - /** @var array operators used in select, null for all operators */ - var $operators; + /** @var Adminer|Plugins */ static $instance; + /** @visibility protected(set) */ public string $error = ''; // HTML /** Name in title and navigation * @return string HTML code */ - function name() { - return "Adminer"; + function name(): string { + return "Adminer"; } /** Connection parameters - * @return array ($server, $username, $password) + * @return array{string, string, string} */ - function credentials() { + function credentials(): array { return array(SERVER, $_GET["username"], get_password()); } /** Get SSL connection options - * @return array array("key" => filename, "cert" => filename, "ca" => filename) or null + * @return string[]|void */ function connectSsl() { } /** Get key used for permanent login - * @param bool - * @return string cryptic string which gets combined with password or false in case of an error + * @return string cryptic string which gets combined with password or '' in case of an error */ - function permanentLogin($create = false) { + function permanentLogin(bool $create = false): string { return password_file($create); } - /** Return key used to group brute force attacks; behind a reverse proxy, you want to return the last part of X-Forwarded-For - * @return string - */ - function bruteForceKey() { + /** Return key used to group brute force attacks; behind a reverse proxy, you want to return the last part of X-Forwarded-For */ + function bruteForceKey(): string { return $_SERVER["REMOTE_ADDR"]; } - + /** Get server name displayed in breadcrumbs - * @param string * @return string HTML code or null */ - function serverName($server) { + function serverName(?string $server): string { return h($server); } - /** Identifier of selected database - * @return string - */ - function database() { + /** Identifier of selected database */ + function database(): ?string { // should be used everywhere instead of DB return DB; } /** Get cached list of databases - * @param bool - * @return array + * @return list */ - function databases($flush = true) { + function databases(bool $flush = true): array { return get_databases($flush); } + /** Print links after list of plugins */ + function pluginsLinks(): void { + } + + /** Operators used in select + * @return list operators + */ + function operators(): array { + return driver()->operators; + } + /** Get list of schemas - * @return array + * @return list */ - function schemas() { + function schemas(): array { return schemas(); } /** Specify limit for waiting on some slow queries like DB list * @return float number of seconds */ - function queryTimeout() { + function queryTimeout(): float { return 2; } - /** Headers to send before HTML output - * @return null - */ - function headers() { + /** Called after connecting and selecting a database */ + function afterConnect(): void { + } + + /** Headers to send before HTML output */ + function headers(): void { } /** Get Content Security Policy headers - * @return array of arrays with directive name in key, allowed sources in value + * @param list $csp of arrays with directive name in key, allowed sources in value + * @return list same as $csp */ - function csp() { - return csp(); + function csp(array $csp): array { + return $csp; } /** Print HTML code inside - * @return bool true to link favicon.ico and adminer.css if exists + * @param bool $dark dark CSS: false to disable, true to force, null to base on user preferences + * @return bool true to link favicon.ico */ - function head() { - ?> - -\n"; + echo ($dark !== false ? "\n" : ""); return true; } + /** Print extra classes in ; must start with a space */ + function bodyClass(): void { + echo " adminer"; + } + /** Get URLs of the CSS files - * @return array of strings + * @return string[] key is URL, value is either 'light' (supports only light color scheme), 'dark' or '' (both) */ - function css() { + function css(): array { $return = array(); - $filename = "adminer.css"; - if (file_exists($filename)) { - $return[] = "$filename?v=" . crc32(file_get_contents($filename)); + foreach (array("", "-dark") as $mode) { + $filename = "adminer$mode.css"; + if (file_exists($filename)) { + $file = file_get_contents($filename); + $return["$filename?v=" . crc32($file)] = ($mode + ? "dark" + : (preg_match('~prefers-color-scheme:\s*dark~', $file) ? '' : 'light') + ); + } } return $return; } - /** Print login form - * @return null - */ - function loginForm() { - global $drivers; - echo "\n"; - echo $this->loginFormField('driver', '
    ' . lang('System') . '', html_select("auth[driver]", $drivers, DRIVER, "loginDriver(this);") . "\n"); - echo $this->loginFormField('server', '
    ' . lang('Server') . '', '' . "\n"); - echo $this->loginFormField('username', '
    ' . lang('Username') . '', '' . script("focus(qs('#username')); qs('#username').form['auth[driver]'].onchange();")); - echo $this->loginFormField('password', '
    ' . lang('Password') . '', '' . "\n"); - echo $this->loginFormField('db', '
    ' . lang('Database') . '', '' . "\n"); + /** Print login form */ + function loginForm(): void { + echo "\n"; + // this is matched by compile.php + echo adminer()->loginFormField('driver', '
    ' . lang('System') . '', html_select("auth[driver]", SqlDriver::$drivers, DRIVER, "loginDriver(this);")); + echo adminer()->loginFormField('server', '
    ' . lang('Server') . '', ''); + // this is matched by compile.php + echo adminer()->loginFormField('username', '
    ' . lang('Username') . '', '' . script("const authDriver = qs('#username').form['auth[driver]']; authDriver && authDriver.onchange();")); + echo adminer()->loginFormField('password', '
    ' . lang('Password') . '', ''); + echo adminer()->loginFormField('db', '
    ' . lang('Database') . '', ''); echo "
    \n"; echo "

    \n"; echo checkbox("auth[permanent]", 1, $_COOKIE["adminer_permanent"], lang('Permanent login')) . "\n"; } - + /** Get login form field - * @param string - * @param string HTML - * @param string HTML - * @return string + * @param string $heading HTML + * @param string $value HTML */ - function loginFormField($name, $heading, $value) { - return $heading . $value; + function loginFormField(string $name, string $heading, string $value): string { + return $heading . $value . "\n"; } /** Authorize the user - * @param string - * @param string * @return mixed true for success, string for error message, false for unknown error */ - function login($login, $password) { + function login(string $login, string $password) { if ($password == "") { return lang('Adminer does not support accessing a database without a password, more information.', target_blank()); } @@ -152,173 +169,186 @@ function login($login, $password) { } /** Table caption used in navigation and headings - * @param array result of SHOW TABLE STATUS + * @param TableStatus $tableStatus * @return string HTML code, "" to ignore table */ - function tableName($tableStatus) { + function tableName(array $tableStatus): string { return h($tableStatus["Name"]); } /** Field caption used in select and edit - * @param array single field returned from fields() - * @param int order of column in select + * @param Field|RoutineField $field + * @param int $order order of column in select * @return string HTML code, "" to ignore field */ - function fieldName($field, $order = 0) { - return '' . h($field["field"]) . ''; + function fieldName(array $field, int $order = 0): string { + $type = $field["full_type"]; + $comment = $field["comment"]; + return '' . h($field["field"]) . ''; } /** Print links after select heading - * @param array result of SHOW TABLE STATUS - * @param string new item options, NULL for no new item - * @return null + * @param TableStatus $tableStatus + * @param ?string $set new item options, NULL for no new item */ - function selectLinks($tableStatus, $set = "") { - global $jush, $driver; + function selectLinks(array $tableStatus, ?string $set = ""): void { + $name = $tableStatus["Name"]; echo '

    \n"; // required for IE9 inline edit - if (!$failed && ($warnings = $driver->warnings())) { + if (!$failed && ($warnings = driver()->warnings())) { $id = "warnings"; $return = ", " . lang('Warnings') . "" . script("qsl('a').onclick = partial(toggle, '$id');", "") . "$return\n" ; } - return "

    " . h(str_replace("\n", " ", $query)) . " (" . format_time($start) . ")" + return "

    " . h(str_replace("\n", " ", $query)) . " (" . format_time($start) . ")" . (support("sql") ? " " . lang('Edit') . "" : "") . $return ; } /** Query printed in SQL command before execution - * @param string query to be executed + * @param string $query query to be executed * @return string escaped query to be printed */ - function sqlCommandQuery($query) - { + function sqlCommandQuery(string $query): string { return shorten_utf8(trim($query), 1000); } + /** Print HTML code just before the Execute button in SQL command */ + function sqlPrintAfter(): void { + } + /** Description of a row in a table - * @param string * @return string SQL expression, empty string for no description */ - function rowDescription($table) { + function rowDescription(string $table): string { return ""; } /** Get descriptions of selected data - * @param array all data to print - * @param array - * @return array + * @param list $rows all data to print + * @param list[] $foreignKeys + * @return list */ - function rowDescriptions($rows, $foreignKeys) { + function rowDescriptions(array $rows, array $foreignKeys): array { return $rows; } /** Get a link to use in select table - * @param string raw value of the field - * @param array single field returned from fields() - * @return string or null to create the default link + * @param string $val raw value of the field + * @param array{type: string} $field + * @return string|void null to create the default link */ - function selectLink($val, $field) { + function selectLink(?string $val, array $field) { } /** Value printed in select table - * @param string HTML-escaped value to print - * @param string link to foreign key - * @param array single field returned from fields() - * @param array original value before applying editVal() and escaping - * @return string - */ - function selectVal($val, $link, $field, $original) { - $return = ($val === null ? "NULL" : (preg_match("~char|binary|boolean~", $field["type"]) && !preg_match("~var~", $field["type"]) ? "$val" : $val)); - if (preg_match('~blob|bytea|raw|file~', $field["type"]) && !is_utf8($val)) { + * @param ?string $val HTML-escaped value to print + * @param ?string $link link to foreign key + * @param array{type: string} $field + * @param string $original original value before applying editVal() and escaping + */ + function selectVal(?string $val, ?string $link, array $field, ?string $original): string { + $return = ($val === null ? "NULL" + : (preg_match("~char|binary|boolean~", $field["type"]) && !preg_match("~var~", $field["type"]) ? "$val" + : (preg_match('~json~', $field["type"]) ? "$val" + : $val) + )); + if (is_blob($field) && !is_utf8($val)) { $return = "" . lang('%d byte(s)', strlen($original)) . ""; } - if (preg_match('~json~', $field["type"])) { - $return = "$return"; - } return ($link ? "$return" : $return); } /** Value conversion used in select and edit - * @param string - * @param array single field returned from fields() - * @return string + * @param array{type: string} $field */ - function editVal($val, $field) { + function editVal(?string $val, array $field): ?string { return $val; } + /** Get configuration options for AdminerConfig + * @return string[] key is config description, value is HTML + */ + function config(): array { + return array(); + } + /** Print table structure in tabular format - * @param array data about individual fields - * @return null + * @param Field[] $fields + * @param TableStatus $tableStatus */ - function tableStructurePrint($fields) { + function tableStructurePrint(array $fields, ?array $tableStatus = null): void { echo "

    \n"; - echo "\n"; + echo "
    \n"; echo "\n"; + $structured_types = driver()->structuredTypes(); foreach ($fields as $field) { - echo " - - -\n"; + echo ($type == "PROCEDURE" ? "\n"; echo script("mixin(qsl('tbody'), {onclick: editingClick, onkeydown: editingKeydown, oninput: editingInput});"); foreach ($fields as $i => $field) { $i++; $orig = $field[($_POST ? "orig" : "field")]; - $display = (isset($_POST["add"][$i-1]) || (isset($field["field"]) && !$_POST["drop_col"][$i])) && (support("drop_col") || $orig == ""); - ?> -> -" . html_select("fields[$i][inout]", explode("|", $inout), $field["inout"]) : ""); ?> -\n", "", $file); + $file = str_replace("\n\n", "", $file); // MS SQL PDO doesn't support EXPLAIN + preg_match_all("~//input\[@value='Login']~", $file, $matches, PREG_OFFSET_CAPTURE); + list($val, $offset) = $matches[0][count($matches[0]) > 1 ? 1 : 0]; // MySQL log-ins three times, we check the second one + $file = substr_replace($file, "\n in select -? Column and table names auto-completition in SQL textarea - http://blog.quplo.com/2010/06/css-code-completion-in-your-browser/ +? Column and table names auto-completion in SQL textarea - http://blog.quplo.com/2010/06/css-code-completion-in-your-browser/ ? Aliasing of built-in functions can save 7 KB, function minification can save 7 KB, substitution of repetitive $a["a"] can save 4 KB, substitution of $_GET and friends can save 2 KB, aliasing of $connection->query can save 24 B, JS Closure compiler can save 2 KB, not enclosing HTML attribute values can save 1.2 KB, replacing \\n by \n can save .3 KB ? Branch binary_compile: LZW compression of all texts can save 11 KB, using chars 127-255 in minification can save 1 KB +https://github.com/vrana/adminer/compare/master...adminneo-org:adminneo:main +Add drag-n-drop moving of rows in table editing +Elasticsearch: New condition operators as the combination of query type and match type + Editor: Three-state checkbox for boolean searches JavaScript data validation - columns containing word email, url, ... @@ -20,12 +23,8 @@ Joining tables - PRIMARY KEY (table, joining) Rank, Tree structure MySQL: -Generated columns (MySQL >= 5.7.6) Data longer than max_allowed_packet can be sent by mysqli_stmt_send_long_data() -MariaDB: -Documentation links - SQLite: Copy tables Delimiter in export and SQL command @@ -35,18 +34,18 @@ PostgreSQL: Display number of schemas in databases overview Users - SELECT * FROM pg_user ORDER BY COUNT(*) -Export - http://www.postgresql.org/docs/8.4/static/functions-info.html Column rights - http://www.postgresql.org/docs/8.4/static/functions-info.html MS SQL: -Display default value Non UTF-8 character sets Detection of table collation -PDO_MSSQL and PDO_SQLSRV driver with seek Oracle: clob comparable with string +CockroachDB: +Link docs to https://www.cockroachlabs.com/docs/v24.3/create-table + SimpleDB: Report invalid user or password Report API calls instead of queries
    " . lang('Column') . "" . lang('Type') . (support("comment") ? "" . lang('Comment') : "") . "
    " . h($field["field"]); - echo "" . h($field["full_type"]) . ""; + echo "
    " . h($field["field"]); + $type = h($field["full_type"]); + $collation = h($field["collation"]); + echo "" + . (in_array($type, (array) $structured_types[lang('User types')]) + ? "$type" + : $type . ($collation && isset($tableStatus["Collation"]) && $collation != $tableStatus["Collation"] ? " $collation" : "")) + . "" + ; echo ($field["null"] ? " NULL" : ""); echo ($field["auto_increment"] ? " " . lang('Auto Increment') . "" : ""); - echo (isset($field["default"]) ? " [" . h($field["default"]) . "]" : ""); + $default = h($field["default"]); + echo (isset($field["default"]) ? " [" . ($field["generated"] ? "$default" : $default) . "]" : ""); echo (support("comment") ? "" . h($field["comment"]) : ""); echo "\n"; } @@ -327,11 +357,16 @@ function tableStructurePrint($fields) { } /** Print list of indexes on table in tabular format - * @param array data about all indexes on a table - * @return null + * @param Index[] $indexes + * @param TableStatus $tableStatus */ - function tableIndexesPrint($indexes) { - echo "\n"; + function tableIndexesPrint(array $indexes, array $tableStatus): void { + $partial = false; + foreach ($indexes as $name => $index) { + $partial |= !!$index["partial"]; + } + echo "
    \n"; + $default_algorithm = first(driver()->indexAlgorithms($tableStatus)); foreach ($indexes as $name => $index) { ksort($index["columns"]); // enforce correct columns order $print = array(); @@ -341,32 +376,36 @@ function tableIndexesPrint($indexes) { . ($index["descs"][$key] ? " DESC" : "") ; } - echo ""; + echo "
    $index[type]" . implode(", ", $print) . "\n"; + + echo "
    $index[type]" . ($default_algorithm && $index['algorithm'] != $default_algorithm ? " ($index[algorithm])" : ""); + echo "" . implode(", ", $print); + if ($partial) { + echo "" . ($index['partial'] ? "WHERE " . h($index['partial']) : ""); + } + echo "\n"; } echo "
    \n"; } /** Print columns box in select - * @param array result of selectColumnsProcess()[0] - * @param array selectable columns - * @return null + * @param list $select result of selectColumnsProcess()[0] + * @param string[] $columns selectable columns */ - function selectColumnsPrint($select, $columns) { - global $functions, $grouping; + function selectColumnsPrint(array $select, array $columns): void { print_fieldset("select", lang('Select'), $select); $i = 0; $select[""] = array(); foreach ($select as $key => $val) { - $val = $_GET["columns"][$key]; + $val = idx($_GET["columns"], $key, array()); $column = select_input( " name='columns[$i][col]'", $columns, $val["col"], ($key !== "" ? "selectFieldChange" : "selectAddRow") ); - echo "
    " . ($functions || $grouping ? "" - . on_help("getTarget(event).value && getTarget(event).value.replace(/ |\$/, '(') + ')'", 1) + echo "
    " . (driver()->functions || driver()->grouping ? html_select("columns[$i][fun]", array(-1 => "") + array_filter(array(lang('Functions') => driver()->functions, lang('Aggregation') => driver()->grouping)), $val["fun"]) + . on_help("event.target.value && event.target.value.replace(/ |\$/, '(') + ')'", 1) . script("qsl('select').onchange = function () { helpClose();" . ($key !== "" ? "" : " qsl('select, input', this.parentNode).onchange();") . " };", "") . "($column)" : $column) . "
    \n"; $i++; @@ -375,25 +414,24 @@ function selectColumnsPrint($select, $columns) { } /** Print search box in select - * @param array result of selectSearchProcess() - * @param array selectable columns - * @param array - * @return null + * @param list $where result of selectSearchProcess() + * @param string[] $columns selectable columns + * @param Index[] $indexes */ - function selectSearchPrint($where, $columns, $indexes) { + function selectSearchPrint(array $where, array $columns, array $indexes): void { print_fieldset("search", lang('Search'), $where); foreach ($indexes as $i => $index) { if ($index["type"] == "FULLTEXT") { - echo "
    (" . implode(", ", array_map('h', $index["columns"])) . ") AGAINST"; - echo " "; + echo "
    (" . implode(", ", array_map('Adminer\h', $index["columns"])) . ") AGAINST"; + echo " "; echo script("qsl('input').oninput = selectFieldChange;", ""); - echo checkbox("boolean[$i]", 1, isset($_GET["boolean"][$i]), "BOOL"); + echo (JUSH == 'sql' ? checkbox("boolean[$i]", 1, isset($_GET["boolean"][$i]), "BOOL") : ''); echo "
    \n"; } } $change_next = "this.parentNode.firstChild.onchange();"; foreach (array_merge((array) $_GET["where"], array(array())) as $i => $val) { - if (!$val || ("$val[col]$val[val]" != "" && in_array($val["op"], $this->operators))) { + if (!$val || ("$val[col]$val[val]" != "" && in_array($val["op"], adminer()->operators()))) { echo "
    " . select_input( " name='where[$i][col]'", $columns, @@ -401,7 +439,7 @@ function selectSearchPrint($where, $columns, $indexes) { ($val ? "selectFieldChange" : "selectAddRow"), "(" . lang('anywhere') . ")" ); - echo html_select("where[$i][op]", $this->operators, $val["op"], $change_next); + echo html_select("where[$i][op]", adminer()->operators(), $val["op"], $change_next); echo ""; echo script("mixin(qsl('input'), {oninput: function () { $change_next }, onkeydown: selectSearchKeydown, onsearch: selectSearchSearch});", ""); echo "
    \n"; @@ -411,12 +449,11 @@ function selectSearchPrint($where, $columns, $indexes) { } /** Print order box in select - * @param array result of selectOrderProcess() - * @param array selectable columns - * @param array - * @return null + * @param list $order result of selectOrderProcess() + * @param string[] $columns selectable columns + * @param Index[] $indexes */ - function selectOrderPrint($order, $columns, $indexes) { + function selectOrderPrint(array $order, array $columns, array $indexes): void { print_fieldset("sort", lang('Sort'), $order); $i = 0; foreach ((array) $_GET["order"] as $key => $val) { @@ -431,22 +468,18 @@ function selectOrderPrint($order, $columns, $indexes) { echo "
    \n"; } - /** Print limit box in select - * @param string result of selectLimitProcess() - * @return null - */ - function selectLimitPrint($limit) { + /** Print limit box in select */ + function selectLimitPrint(int $limit): void { echo "
    " . lang('Limit') . "
    "; //
    for easy styling - echo ""; + echo ""; echo script("qsl('input').oninput = selectFieldChange;", ""); echo "
    \n"; } /** Print text length box in select - * @param string result of selectLengthProcess() - * @return null + * @param numeric-string $text_length result of selectLengthProcess() */ - function selectLengthPrint($text_length) { + function selectLengthPrint(string $text_length): void { if ($text_length !== null) { echo "
    " . lang('Text length') . "
    "; echo ""; @@ -455,15 +488,14 @@ function selectLengthPrint($text_length) { } /** Print action box in select - * @param array - * @return null + * @param Index[] $indexes */ - function selectActionPrint($indexes) { + function selectActionPrint(array $indexes): void { echo "
    " . lang('Action') . "
    "; echo ""; echo " "; echo "\n"; - echo "var indexColumns = "; + echo "const indexColumns = "; $columns = array(); foreach ($indexes as $index) { $current_key = reset($index["columns"]); @@ -480,42 +512,40 @@ function selectActionPrint($indexes) { echo "\n"; echo "
    \n"; } - + /** Print command box in select * @return bool whether to print default commands */ - function selectCommandPrint() { + function selectCommandPrint(): bool { return !information_schema(DB); } /** Print import box in select * @return bool whether to print default import */ - function selectImportPrint() { + function selectImportPrint(): bool { return !information_schema(DB); } /** Print extra text in the end of a select form - * @param array fields holding e-mails - * @param array selectable columns - * @return null + * @param string[] $emailFields fields holding e-mails + * @param string[] $columns selectable columns */ - function selectEmailPrint($emailFields, $columns) { + function selectEmailPrint(array $emailFields, array $columns): void { } /** Process columns box in select - * @param array selectable columns - * @param array - * @return array (array(select_expressions), array(group_expressions)) + * @param string[] $columns selectable columns + * @param Index[] $indexes + * @return list> [[select_expressions], [group_expressions]] */ - function selectColumnsProcess($columns, $indexes) { - global $functions, $grouping; + function selectColumnsProcess(array $columns, array $indexes): array { $select = array(); // select expressions, empty for * $group = array(); // expressions without aggregation - will be used for GROUP BY if an aggregation function is used foreach ((array) $_GET["columns"] as $key => $val) { - if ($val["fun"] == "count" || ($val["col"] != "" && (!$val["fun"] || in_array($val["fun"], $functions) || in_array($val["fun"], $grouping)))) { + if ($val["fun"] == "count" || ($val["col"] != "" && (!$val["fun"] || in_array($val["fun"], driver()->functions) || in_array($val["fun"], driver()->grouping)))) { $select[$key] = apply_sql_function($val["fun"], ($val["col"] != "" ? idf_escape($val["col"]) : "*")); - if (!in_array($val["fun"], $grouping)) { + if (!in_array($val["fun"], driver()->grouping)) { $group[] = $select[$key]; } } @@ -524,63 +554,62 @@ function selectColumnsProcess($columns, $indexes) { } /** Process search box in select - * @param array - * @param array - * @return array expressions to join by AND + * @param Field[] $fields + * @param Index[] $indexes + * @return list expressions to join by AND */ - function selectSearchProcess($fields, $indexes) { - global $connection, $driver; + function selectSearchProcess(array $fields, array $indexes): array { $return = array(); foreach ($indexes as $i => $index) { - if ($index["type"] == "FULLTEXT" && $_GET["fulltext"][$i] != "") { - $return[] = "MATCH (" . implode(", ", array_map('idf_escape', $index["columns"])) . ") AGAINST (" . q($_GET["fulltext"][$i]) . (isset($_GET["boolean"][$i]) ? " IN BOOLEAN MODE" : "") . ")"; + if ($index["type"] == "FULLTEXT" && idx($_GET["fulltext"], $i) != "") { + $return[] = "MATCH (" . implode(", ", array_map('Adminer\idf_escape', $index["columns"])) . ") AGAINST (" . q($_GET["fulltext"][$i]) . (isset($_GET["boolean"][$i]) ? " IN BOOLEAN MODE" : "") . ")"; } } foreach ((array) $_GET["where"] as $key => $val) { - if ("$val[col]$val[val]" != "" && in_array($val["op"], $this->operators)) { - $prefix = ""; - $cond = " $val[op]"; - if (preg_match('~IN$~', $val["op"])) { - $in = process_length($val["val"]); - $cond .= " " . ($in != "" ? $in : "(NULL)"); - } elseif ($val["op"] == "SQL") { - $cond = " $val[val]"; // SQL injection - } elseif ($val["op"] == "LIKE %%") { - $cond = " LIKE " . $this->processInput($fields[$val["col"]], "%$val[val]%"); - } elseif ($val["op"] == "ILIKE %%") { - $cond = " ILIKE " . $this->processInput($fields[$val["col"]], "%$val[val]%"); - } elseif ($val["op"] == "FIND_IN_SET") { - $prefix = "$val[op](" . q($val["val"]) . ", "; - $cond = ")"; - } elseif (!preg_match('~NULL$~', $val["op"])) { - $cond .= " " . $this->processInput($fields[$val["col"]], $val["val"]); - } - if ($val["col"] != "") { - $return[] = $prefix . $driver->convertSearch(idf_escape($val["col"]), $val, $fields[$val["col"]]) . $cond; - } else { - // find anywhere - $cols = array(); - foreach ($fields as $name => $field) { - if ((preg_match('~^[-\d.' . (preg_match('~IN$~', $val["op"]) ? ',' : '') . ']+$~', $val["val"]) || !preg_match('~' . number_type() . '|bit~', $field["type"])) - && (!preg_match("~[\x80-\xFF]~", $val["val"]) || preg_match('~char|text|enum|set~', $field["type"])) - && (!preg_match('~date|timestamp~', $field["type"]) || preg_match('~^\d+-\d+-\d+~', $val["val"])) - ) { - $cols[] = $prefix . $driver->convertSearch(idf_escape($name), $val, $field) . $cond; - } + $col = $val["col"]; + if ("$col$val[val]" != "" && in_array($val["op"], adminer()->operators())) { + $conds = array(); + foreach (($col != "" ? array($col => $fields[$col]) : $fields) as $name => $field) { + $prefix = ""; + $cond = " $val[op]"; + if (preg_match('~IN$~', $val["op"])) { + $in = process_length($val["val"]); + $cond .= " " . ($in != "" ? $in : "(NULL)"); + } elseif ($val["op"] == "SQL") { + $cond = " $val[val]"; // SQL injection + } elseif (preg_match('~^(I?LIKE) %%$~', $val["op"], $match)) { + $cond = " $match[1] " . adminer()->processInput($field, "%$val[val]%"); + } elseif ($val["op"] == "FIND_IN_SET") { + $prefix = "$val[op](" . q($val["val"]) . ", "; + $cond = ")"; + } elseif (!preg_match('~NULL$~', $val["op"])) { + $cond .= " " . adminer()->processInput($field, $val["val"]); + } + if ($col != "" || ( // find anywhere + isset($field["privileges"]["where"]) + && (preg_match('~^[-\d.' . (preg_match('~IN$~', $val["op"]) ? ',' : '') . ']+$~', $val["val"]) || !preg_match('~' . number_type() . '|bit~', $field["type"])) + && (!preg_match("~[\x80-\xFF]~", $val["val"]) || preg_match('~char|text|enum|set~', $field["type"])) + && (!preg_match('~date|timestamp~', $field["type"]) || preg_match('~^\d+-\d+-\d+~', $val["val"])) + )) { + $conds[] = $prefix . driver()->convertSearch(idf_escape($name), $val, $field) . $cond; } - $return[] = ($cols ? "(" . implode(" OR ", $cols) . ")" : "1 = 0"); } + $return[] = + (count($conds) == 1 ? $conds[0] : + ($conds ? "(" . implode(" OR ", $conds) . ")" : + "1 = 0" + )); } } return $return; } /** Process order box in select - * @param array - * @param array - * @return array expressions to join by comma + * @param Field[] $fields + * @param Index[] $indexes + * @return list expressions to join by comma */ - function selectOrderProcess($fields, $indexes) { + function selectOrderProcess(array $fields, array $indexes): array { $return = array(); foreach ((array) $_GET["order"] as $key => $val) { if ($val != "") { @@ -592,53 +621,48 @@ function selectOrderProcess($fields, $indexes) { return $return; } - /** Process limit box in select - * @return string expression to use in LIMIT, will be escaped - */ - function selectLimitProcess() { - return (isset($_GET["limit"]) ? $_GET["limit"] : "50"); + /** Process limit box in select */ + function selectLimitProcess(): int { + return (isset($_GET["limit"]) ? intval($_GET["limit"]) : 50); } /** Process length box in select - * @return string number of characters to shorten texts, will be escaped + * @return numeric-string number of characters to shorten texts, will be escaped, empty string means unlimited */ - function selectLengthProcess() { - return (isset($_GET["text_length"]) ? $_GET["text_length"] : "100"); + function selectLengthProcess(): string { + return (isset($_GET["text_length"]) ? "$_GET[text_length]" : "100"); } /** Process extras in select form - * @param array AND conditions - * @param array + * @param string[] $where AND conditions + * @param list[] $foreignKeys * @return bool true if processed, false to process other parts of form */ - function selectEmailProcess($where, $foreignKeys) { + function selectEmailProcess(array $where, array $foreignKeys): bool { return false; } /** Build SQL query used in select - * @param array result of selectColumnsProcess()[0] - * @param array result of selectSearchProcess() - * @param array result of selectColumnsProcess()[1] - * @param array result of selectOrderProcess() - * @param int result of selectLimitProcess() - * @param int index of page starting at zero + * @param list $select result of selectColumnsProcess()[0] + * @param list $where result of selectSearchProcess() + * @param list $group result of selectColumnsProcess()[1] + * @param list $order result of selectOrderProcess() + * @param int $limit result of selectLimitProcess() + * @param int $page index of page starting at zero * @return string empty string to use default query */ - function selectQueryBuild($select, $where, $group, $order, $limit, $page) { + function selectQueryBuild(array $select, array $where, array $group, array $order, int $limit, ?int $page): string { return ""; } /** Query printed after execution in the message - * @param string executed query - * @param string elapsed time - * @param bool - * @return string + * @param string $query executed query + * @param string $time elapsed time */ - function messageQuery($query, $time, $failed = false) { - global $jush, $driver; + function messageQuery(string $query, string $time, bool $failed = false): string { restart_session(); $history = &get_session("queries"); - if (!$history[$_GET["db"]]) { + if (!idx($history, $_GET["db"])) { $history[$_GET["db"]] = array(); } if (strlen($query) > 1e6) { @@ -646,13 +670,13 @@ function messageQuery($query, $time, $failed = false) { } $history[$_GET["db"]][] = array($query, time(), $time); // not DB - $_GET["db"] is changed in database.inc.php //! respect $_GET["ns"] $sql_id = "sql-" . count($history[$_GET["db"]]); - $return = "" . lang('SQL command') . "\n"; - if (!$failed && ($warnings = $driver->warnings())) { + $return = "" . lang('SQL command') . " 🗐\n"; + if (!$failed && ($warnings = driver()->warnings())) { $id = "warnings-" . count($history[$_GET["db"]]); $return = "" . lang('Warnings') . ", $return\n"; } return " " . @date("H:i:s") . "" // @ - time zone may be not set - . " $return
    " + : " size='10' value='" . h($value) . "' placeholder='$placeholder'>" + ) . ($onchange ? script("qsl('$tag').onchange = $onchange;", "") : ""); //! use oninput for input } -if ($foreign_keys) { - $structured_types[lang('Foreign keys')] = $foreign_keys; -} -echo optionlist(array_merge($extra_types, $structured_types), $type); -?>" size="3" aria-labelledby="label-length">"; + echo ""; + echo ""; + echo ($collations + ? "" + : '' + ); + echo (driver()->unsigned ? "' : ''); + echo (isset($field['on_update']) ? "' : '' + ); + echo ($foreign_keys + ? " " + : " " // space for IE + ); +} + +/** Filter length value including enums */ +function process_length(?string $length): string { + $enum_length = driver()->enumLength; return (preg_match("~^\\s*\\(?\\s*$enum_length(?:\\s*,\\s*$enum_length)*+\\s*\\)?\\s*\$~", $length) && preg_match_all("~$enum_length~", $length, $matches) ? "(" . implode(",", $matches[0]) . ")" : preg_replace('~^[0-9].*~', '(\0)', preg_replace('~[^-0-9,+()[\]]~', '', $length)) @@ -202,25 +204,26 @@ function process_length($length) { } /** Create SQL string from field type -* @param array -* @param string -* @return string +* @param FieldType $field */ -function process_type($field, $collate = "COLLATE") { - global $unsigned; +function process_type(array $field, string $collate = "COLLATE"): string { return " $field[type]" . process_length($field["length"]) - . (preg_match(number_type(), $field["type"]) && in_array($field["unsigned"], $unsigned) ? " $field[unsigned]" : "") - . (preg_match('~char|text|enum|set~', $field["type"]) && $field["collation"] ? " $collate " . q($field["collation"]) : "") + . (preg_match(number_type(), $field["type"]) && in_array($field["unsigned"], driver()->unsigned) ? " $field[unsigned]" : "") + . (preg_match('~char|text|enum|set~', $field["type"]) && $field["collation"] ? " $collate " . (JUSH == "mssql" ? $field["collation"] : q($field["collation"])) : "") ; } /** Create SQL string from field -* @param array basic field information -* @param array information about field type -* @return array array("field", "type", "NULL", "DEFAULT", "ON UPDATE", "COMMENT", "AUTO_INCREMENT") +* @param Field $field basic field information +* @param Field $type_field information about field type +* @return list ["field", "type", "NULL", "DEFAULT", "ON UPDATE", "COMMENT", "AUTO_INCREMENT"] */ -function process_field($field, $type_field) { +function process_field(array $field, array $type_field): array { + // MariaDB exports CURRENT_TIMESTAMP as a function. + if ($field["on_update"]) { + $field["on_update"] = str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", $field["on_update"]); + } return array( idf_escape(trim($field["field"])), process_type($type_field), @@ -233,25 +236,35 @@ function process_field($field, $type_field) { } /** Get default value clause -* @param array -* @return string +* @param Field $field */ -function default_value($field) { - $default = $field["default"]; - return ($default === null ? "" : " DEFAULT " . (preg_match('~char|binary|text|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default) ? q($default) : $default)); +function default_value(array $field): string { + if ($field["default"] === null) { + return ""; + } + $default = str_replace("\r", "", $field["default"]); + $generated = $field["generated"]; + return (in_array($generated, driver()->generated) + ? (JUSH == "mssql" ? " AS ($default)" . ($generated == "VIRTUAL" ? "" : " $generated") : " GENERATED ALWAYS AS ($default) $generated") + : " DEFAULT " . (!preg_match('~^GENERATED ~i', $default) && (preg_match('~char|binary|text|json|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default)) + ? (JUSH == "sql" && preg_match('~text|json~', $field["type"]) ? "(" . q($default) . ")" : q($default)) // MySQL requires () around default value of text column + : str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", (JUSH == "sqlite" ? "($default)" : $default)) + ) + ); } /** Get type class to use in CSS -* @param string -* @return string class='' +* @return string|void class='' */ -function type_class($type) { - foreach (array( - 'char' => 'text', - 'date' => 'time|year', - 'binary' => 'blob', - 'enum' => 'set', - ) as $key => $val) { +function type_class(string $type) { + foreach ( + array( + 'char' => 'text', + 'date' => 'time|year', + 'binary' => 'blob', + 'enum' => 'set', + ) as $key => $val + ) { if (preg_match("~$key|$val~", $type)) { return " class='$key'"; } @@ -259,71 +272,74 @@ function type_class($type) { } /** Print table interior for fields editing -* @param array -* @param array -* @param string TABLE or PROCEDURE -* @param array returned by referencable_primary() -* @return null +* @param (Field|RoutineField)[] $fields +* @param list $collations +* @param 'TABLE'|'PROCEDURE' $type +* @param string[] $foreign_keys */ -function edit_fields($fields, $collations, $type = "TABLE", $foreign_keys = array()) { - global $inout; +function edit_fields(array $fields, array $collations, $type = "TABLE", array $foreign_keys = array()): void { $fields = array_values($fields); - $default_class = (($_POST ? $_POST["defaults"] : adminer_setting("defaults")) ? "" : " class='hidden'"); - $comment_class = (($_POST ? $_POST["comments"] : adminer_setting("comments")) ? "" : " class='hidden'"); - ?> -
    - - - - - -NULL -AI "example-auto-increment.html", - 'mariadb' => "auto_increment/", - 'sqlite' => "autoinc.html", - 'pgsql' => "datatype.html#DATATYPE-SERIAL", - 'mssql' => "ms186775.aspx", -)); ?> -> -" . lang('Comment') : ""); ?> - -" . script("row_count = " . count($fields) . ";"); ?> -
    " : ""); + echo "" . ($type == "TABLE" ? lang('Column name') : lang('Parameter name')); + echo "" . lang('Type') . "" . script("qs('#enum-edit').onblur = editingLengthBlur;"); + echo "" . lang('Length'); + echo "" . lang('Options'); // no label required, options have their own label + if ($type == "TABLE") { + echo "NULL\n"; + echo "AI"; + echo doc_link(array( + 'sql' => "example-auto-increment.html", + 'mariadb' => "auto_increment/", + 'sqlite' => "autoinc.html", + 'pgsql' => "datatype-numeric.html#DATATYPE-SERIAL", + 'mssql' => "t-sql/statements/create-table-transact-sql-identity-property", + )); + echo "" . lang('Default value'); + echo (support("comment") ? "" . lang('Comment') : ""); + } + echo "" . icon("plus", "add[" . (support("move_col") ? 0 : count($fields)) . "]", "+", lang('Add next')); + echo "\n
    " data-maxlength="64" autocapitalize="off" aria-labelledby="label-name"> - - - ->" aria-labelledby="label-default">\n"; + echo ($type == "PROCEDURE" ? "" . html_select("fields[$i][inout]", explode("|", driver()->inout), $field["inout"]) : "") . ""; + if ($display) { + echo ""; + } + echo input_hidden("fields[$i][orig]", $orig); + edit_type("fields[$i]", $field, $collations, $foreign_keys); + if ($type == "TABLE") { + echo "" . checkbox("fields[$i][null]", 1, $field["null"], "", "", "block", "label-null"); + echo ""; + echo "" . (driver()->generated + ? html_select("fields[$i][generated]", array_merge(array("", "DEFAULT"), driver()->generated), $field["generated"]) . " " + : checkbox("fields[$i][generated]", 1, $field["generated"], "", "", "", "label-default") + ); + $attrs = " name='fields[$i][default]' aria-labelledby='label-default'"; + $value = h($field["default"]); + echo (preg_match('~\n~', $field["default"]) ? "\n$value" : ""); // \n to preserve the leading newline echo (support("comment") ? "" : ""); } echo ""; echo (support("move_col") ? - " " - . " " - . " " + icon("plus", "add[$i]", "+", lang('Add next')) . " " + . icon("up", "up[$i]", "↑", lang('Move up')) . " " + . icon("down", "down[$i]", "↓", lang('Move down')) . " " : ""); - echo ($orig == "" || support("drop_col") ? "" : ""); + echo ($orig == "" || support("drop_col") ? icon("cross", "drop_col[$i]", "x", lang('Remove')) : ""); } } /** Move fields up and down or add field -* @param array -* @return bool +* @param Field[] $fields */ -function process_fields(&$fields) { +function process_fields(array &$fields): bool { $offset = 0; if ($_POST["up"]) { $last = 0; @@ -361,21 +377,19 @@ function process_fields(&$fields) { } /** Callback used in routine() -* @param array -* @return string +* @param list $match */ -function normalize_enum($match) { - return "'" . str_replace("'", "''", addcslashes(stripcslashes(str_replace($match[0][0] . $match[0][0], $match[0][0], substr($match[0], 1, -1))), '\\')) . "'"; +function normalize_enum(array $match): string { + $val = $match[0]; + return "'" . str_replace("'", "''", addcslashes(stripcslashes(str_replace($val[0] . $val[0], $val[0], substr($val, 1, -1))), '\\')) . "'"; } /** Issue grant or revoke commands -* @param string GRANT or REVOKE -* @param array -* @param string -* @param string -* @return bool +* @param 'GRANT'|'REVOKE' $grant +* @param list $privileges +* @return Result|bool */ -function grant($grant, $privileges, $columns, $on) { +function grant(string $grant, array $privileges, ?string $columns, string $on) { if (!$privileges) { return true; } @@ -390,20 +404,14 @@ function grant($grant, $privileges, $columns, $on) { } /** Drop old object and create a new one -* @param string drop old object query -* @param string create new object query -* @param string drop new object query -* @param string create test object query -* @param string drop test object query -* @param string -* @param string -* @param string -* @param string -* @param string -* @param string -* @return null redirect in success +* @param string $drop drop old object query +* @param string $create create new object query +* @param string $drop_created drop new object query +* @param string $test create test object query +* @param string $drop_test drop test object query +* @return void redirect on success */ -function drop_create($drop, $create, $drop_created, $test, $drop_test, $location, $message_drop, $message_alter, $message_create, $old_name, $new_name) { +function drop_create(string $drop, string $create, string $drop_created, string $test, string $drop_test, string $location, string $message_drop, string $message_alter, string $message_create, string $old_name, string $new_name): void { if ($_POST["drop"]) { query_redirect($drop, $location, $message_drop); } elseif ($old_name == "") { @@ -424,78 +432,68 @@ function drop_create($drop, $create, $drop_created, $test, $drop_test, $location } /** Generate SQL query for creating trigger -* @param string -* @param array result of trigger() -* @return string +* @param Trigger $row */ -function create_trigger($on, $row) { - global $jush; +function create_trigger(string $on, array $row): string { $timing_event = " $row[Timing] $row[Event]" . (preg_match('~ OF~', $row["Event"]) ? " $row[Of]" : ""); // SQL injection return "CREATE TRIGGER " . idf_escape($row["Trigger"]) - . ($jush == "mssql" ? $on . $timing_event : $timing_event . $on) + . (JUSH == "mssql" ? $on . $timing_event : $timing_event . $on) . rtrim(" $row[Type]\n$row[Statement]", ";") . ";" ; } /** Generate SQL query for creating routine -* @param string "PROCEDURE" or "FUNCTION" -* @param array result of routine() -* @return string +* @param 'PROCEDURE'|'FUNCTION' $routine +* @param Routine $row */ -function create_routine($routine, $row) { - global $inout, $jush; +function create_routine($routine, array $row): string { $set = array(); $fields = (array) $row["fields"]; ksort($fields); // enforce fields order foreach ($fields as $field) { if ($field["field"] != "") { - $set[] = (preg_match("~^($inout)\$~", $field["inout"]) ? "$field[inout] " : "") . idf_escape($field["field"]) . process_type($field, "CHARACTER SET"); + $set[] = (preg_match("~^(" . driver()->inout . ")\$~", $field["inout"]) ? "$field[inout] " : "") . idf_escape($field["field"]) . process_type($field, "CHARACTER SET"); } } - $definition = rtrim("\n$row[definition]", ";"); + $definition = rtrim($row["definition"], ";"); return "CREATE $routine " . idf_escape(trim($row["name"])) . " (" . implode(", ", $set) . ")" - . (isset($_GET["function"]) ? " RETURNS" . process_type($row["returns"], "CHARACTER SET") : "") + . ($routine == "FUNCTION" ? " RETURNS" . process_type($row["returns"], "CHARACTER SET") : "") . ($row["language"] ? " LANGUAGE $row[language]" : "") - . ($jush == "pgsql" ? " AS " . q($definition) : "$definition;") + . (JUSH == "pgsql" ? " AS " . q($definition) : "\n$definition;") ; } -/** Remove current user definer from SQL command -* @param string -* @return string -*/ -function remove_definer($query) { +/** Remove current user definer from SQL command */ +function remove_definer(string $query): string { return preg_replace('~^([A-Z =]+) DEFINER=`' . preg_replace('~@(.*)~', '`@`(%|\1)', logged_user()) . '`~', '\1', $query); //! proper escaping of user } /** Format foreign key to use in SQL query -* @param array ("db" => string, "ns" => string, "table" => string, "source" => array, "target" => array, "on_delete" => one of $on_actions, "on_update" => one of $on_actions) -* @return string +* @param ForeignKey $foreign_key */ -function format_foreign_key($foreign_key) { - global $on_actions; +function format_foreign_key(array $foreign_key): string { $db = $foreign_key["db"]; $ns = $foreign_key["ns"]; - return " FOREIGN KEY (" . implode(", ", array_map('idf_escape', $foreign_key["source"])) . ") REFERENCES " + return " FOREIGN KEY (" . implode(", ", array_map('Adminer\idf_escape', $foreign_key["source"])) . ") REFERENCES " . ($db != "" && $db != $_GET["db"] ? idf_escape($db) . "." : "") . ($ns != "" && $ns != $_GET["ns"] ? idf_escape($ns) . "." : "") - . table($foreign_key["table"]) - . " (" . implode(", ", array_map('idf_escape', $foreign_key["target"])) . ")" //! reuse $name - check in older MySQL versions - . (preg_match("~^($on_actions)\$~", $foreign_key["on_delete"]) ? " ON DELETE $foreign_key[on_delete]" : "") - . (preg_match("~^($on_actions)\$~", $foreign_key["on_update"]) ? " ON UPDATE $foreign_key[on_update]" : "") + . idf_escape($foreign_key["table"]) + . " (" . implode(", ", array_map('Adminer\idf_escape', $foreign_key["target"])) . ")" //! reuse $name - check in older MySQL versions + . (preg_match("~^(" . driver()->onActions . ")\$~", $foreign_key["on_delete"]) ? " ON DELETE $foreign_key[on_delete]" : "") + . (preg_match("~^(" . driver()->onActions . ")\$~", $foreign_key["on_update"]) ? " ON UPDATE $foreign_key[on_update]" : "") + . ($foreign_key["deferrable"] ? " $foreign_key[deferrable]" : "") ; } /** Add a file to TAR -* @param string -* @param TmpFile -* @return null prints the output +* @param TmpFile $tmp_file +* @return void prints the output */ -function tar_file($filename, $tmp_file) { +function tar_file(string $filename, $tmp_file): void { $return = pack("a100a8a8a8a12a12", $filename, 644, 0, 0, decoct($tmp_file->size), decoct(time())); $checksum = 8*32; // space for checksum itself for ($i=0; $i < strlen($return); $i++) { @@ -508,59 +506,33 @@ function tar_file($filename, $tmp_file) { echo str_repeat("\0", 511 - ($tmp_file->size + 511) % 512); } -/** Get INI bytes value -* @param string -* @return int -*/ -function ini_bytes($ini) { - $val = ini_get($ini); - switch (strtolower(substr($val, -1))) { - case 'g': $val *= 1024; // no break - case 'm': $val *= 1024; // no break - case 'k': $val *= 1024; - } - return $val; -} - /** Create link to database documentation -* @param array $jush => $path -* @param string HTML code +* @param string[] $paths JUSH => $path +* @param string $text HTML code * @return string HTML code */ -function doc_link($paths, $text = "?") { - global $jush, $connection; - $server_info = $connection->server_info; +function doc_link(array $paths, string $text = "?"): string { + $server_info = connection()->server_info; $version = preg_replace('~^(\d\.?\d).*~s', '\1', $server_info); // two most significant digits $urls = array( 'sql' => "https://dev.mysql.com/doc/refman/$version/en/", 'sqlite' => "https://www.sqlite.org/", - 'pgsql' => "https://www.postgresql.org/docs/$version/", - 'mssql' => "https://msdn.microsoft.com/library/", + 'pgsql' => "https://www.postgresql.org/docs/" . (connection()->flavor == 'cockroach' ? "current" : $version) . "/", + 'mssql' => "https://learn.microsoft.com/en-us/sql/", 'oracle' => "https://www.oracle.com/pls/topic/lookup?ctx=db" . preg_replace('~^.* (\d+)\.(\d+)\.\d+\.\d+\.\d+.*~s', '\1\2', $server_info) . "&id=", ); - if (preg_match('~MariaDB~', $server_info)) { - $urls['sql'] = "https://mariadb.com/kb/en/library/"; + if (connection()->flavor == 'maria') { + $urls['sql'] = "https://mariadb.com/kb/en/"; $paths['sql'] = (isset($paths['mariadb']) ? $paths['mariadb'] : str_replace(".html", "/", $paths['sql'])); } - return ($paths[$jush] ? "$text" : ""); -} - -/** Wrap gzencode() for usage in ob_start() -* @param string -* @return string -*/ -function ob_gzencode($string) { - // ob_start() callback recieves an optional parameter $phase but gzencode() accepts optional parameter $level - return gzencode($string); + return ($paths[JUSH] ? "$text" : ""); } /** Compute size of database -* @param string * @return string formatted */ -function db_size($db) { - global $connection; - if (!$connection->select_db($db)) { +function db_size(string $db): string { + if (!connection()->select_db($db)) { return "?"; } $return = 0; @@ -570,15 +542,11 @@ function db_size($db) { return format_number($return); } -/** Print SET NAMES if utf8mb4 might be needed -* @param string -* @return null -*/ -function set_utf8mb4($create) { - global $connection; +/** Print SET NAMES if utf8mb4 might be needed */ +function set_utf8mb4(string $create): void { static $set = false; if (!$set && preg_match('~\butf8mb4~i', $create)) { // possible false positive $set = true; - echo "SET NAMES " . charset($connection) . ";\n\n"; + echo "SET NAMES " . charset(connection()) . ";\n\n"; } } diff --git a/adminer/include/errors.inc.php b/adminer/include/errors.inc.php new file mode 100644 index 000000000..ff7fa313e --- /dev/null +++ b/adminer/include/errors.inc.php @@ -0,0 +1,9 @@ +credentials(); + $return = Driver::connect($credentials[0], $credentials[1], $credentials[2]); + return (is_object($return) ? $return : null); } /** Unescape database identifier -* @param string text inside `` -* @return string +* @param string $idf text inside `` */ -function idf_unescape($idf) { - if (!preg_match('~^[`\'"]~', $idf)) { +function idf_unescape(string $idf): string { + if (!preg_match('~^[`\'"[]~', $idf)) { return $idf; } $last = substr($idf, -1); return str_replace($last . $last, $last, substr($idf, 1, -1)); } -/** Escape string to use inside '' -* @param string -* @return string -*/ -function escape_string($val) { +/** Shortcut for connection()->quote($string) */ +function q(string $string): string { + return connection()->quote($string); +} + +/** Escape string to use inside '' */ +function escape_string(string $val): string { return substr(q($val), 1, -1); } -/** Remove non-digits from a string -* @param string -* @return string +/** Get a possibly missing item from a possibly missing array +* idx($row, $key) is better than $row[$key] ?? null because PHP will report error for undefined $row +* @param ?mixed[] $array +* @param array-key $key +* @param mixed $default +* @return mixed */ -function number($val) { - return preg_replace('~[^0-9]+~', '', $val); +function idx(?array $array, $key, $default = null) { + return ($array && array_key_exists($key, $array) ? $array[$key] : $default); } -/** Get regular expression to match numeric types -* @return string +/** Remove non-digits from a string; used instead of intval() to not corrupt big numbers +* @return numeric-string */ -function number_type() { +function number(string $val): string { + return preg_replace('~[^0-9]+~', '', $val); +} + +/** Get regular expression to match numeric types */ +function number_type(): string { return '((? $v) { - unset($process[$key][$k]); - if (is_array($v)) { - $process[$key][stripslashes($k)] = $v; - $process[] = &$process[$key][stripslashes($k)]; - } else { - $process[$key][stripslashes($k)] = ($filter ? $v : stripslashes($v)); - } - } - } +function remove_slashes(array $values, bool $filter = false): array { + $return = array(); + foreach ($values as $key => $val) { + $return[stripslashes($key)] = (is_array($val) + ? remove_slashes($val, $filter) + : ($filter ? $val : stripslashes($val)) + ); } + return $return; } -/** Escape or unescape string to use inside form [] -* @param string -* @param bool -* @return string -*/ -function bracket_escape($idf, $back = false) { +/** Escape or unescape string to use inside form [] */ +function bracket_escape(string $idf, bool $back = false): string { // escape brackets inside name="x[]" static $trans = array(':' => ':1', ']' => ':2', '[' => ':3', '"' => ':4'); return strtr($idf, ($back ? array_flip($trans) : $trans)); } /** Check if connection has at least the given version -* @param string required version -* @param string required MariaDB version -* @param Min_DB defaults to $connection -* @return bool +* @param string|float $version required version +* @param string|float $maria_db required MariaDB version */ -function min_version($version, $maria_db = "", $connection2 = null) { - global $connection; - if (!$connection2) { - $connection2 = $connection; - } +function min_version($version, $maria_db = "", ?Db $connection2 = null): bool { + $connection2 = connection($connection2); $server_info = $connection2->server_info; if ($maria_db && preg_match('~([\d.]+)-MariaDB~', $server_info, $match)) { $server_info = $match[1]; $version = $maria_db; } - return (version_compare($server_info, $version) >= 0); + return $version && version_compare($server_info, $version) >= 0; } -/** Get connection charset -* @param Min_DB -* @return string -*/ -function charset($connection) { +/** Get connection charset */ +function charset(Db $connection): string { return (min_version("5.5.3", 0, $connection) ? "utf8mb4" : "utf8"); // SHOW CHARSET would require an extra query } -/** Return $trailing"; -} - -/** Return \n"; -} - -/** Get a nonce="" attribute with CSP nonce -* @return string -*/ -function nonce() { - return ' nonce="' . get_nonce() . '"'; -} - -/** Get a target="_blank" attribute -* @return string -*/ -function target_blank() { - return ' target="_blank" rel="noreferrer noopener"'; -} - -/** Escape for HTML -* @param string -* @return string -*/ -function h($string) { - return str_replace("\0", "�", htmlspecialchars($string, ENT_QUOTES, 'utf-8')); -} - -/** Convert \n to
    -* @param string -* @return string -*/ -function nl_br($string) { - return str_replace("\n", "
    ", $string); // nl2br() uses XHTML before PHP 5.3 -} - -/** Generate HTML checkbox -* @param string -* @param string -* @param bool -* @param string -* @param string -* @param string -* @param string -* @return string -*/ -function checkbox($name, $value, $checked, $label = "", $onclick = "", $class = "", $labelled_by = "") { - $return = "" - . ($onclick ? script("qsl('input').onclick = function () { $onclick };", "") : "") - ; - return ($label != "" || $class ? "$return" . h($label) . "" : $return); -} - -/** Generate list of HTML options -* @param array array of strings or arrays (creates optgroup) -* @param mixed -* @param bool always use array keys for value="", otherwise only string keys are used -* @return string -*/ -function optionlist($options, $selected = null, $use_keys = false) { - $return = ""; - foreach ($options as $k => $v) { - $opts = array($k => $v); - if (is_array($v)) { - $return .= ''; - $opts = $v; - } - foreach ($opts as $key => $val) { - $return .= '' . h($val); - } - if (is_array($v)) { - $return .= ''; - } - } - return $return; -} - -/** Generate HTML radio list -* @param string -* @param array -* @param string -* @param string true for no onchange, false for radio -* @param string -* @return string -*/ -function html_select($name, $options, $value = "", $onchange = true, $labelled_by = "") { - if ($onchange) { - return "" - . (is_string($onchange) ? script("qsl('select').onchange = function () { $onchange };", "") : "") - ; - } - $return = ""; - foreach ($options as $key => $val) { - $return .= ""; - } - return $return; -} - -/** Generate HTML if $options are empty -* @param string -* @param array -* @param string -* @param string -* @param string -* @return string -*/ -function select_input($attrs, $options, $value = "", $onchange = "", $placeholder = "") { - $tag = ($options ? "select" : "input"); - return "<$tag$attrs" . ($options - ? ">
    open/coverage.php?coverage=0
    clicklink=Explain
    verifyTextPresentClustered Index Scan
    verifyTextPresentPDO_", $offset + strlen($val), 0); + file_put_contents("pdo-$filename", $file); + } +} diff --git a/tests/katalon.html b/tests/katalon.html deleted file mode 100644 index 2f571caae..000000000 --- a/tests/katalon.html +++ /dev/null @@ -1,1599 +0,0 @@ - - - - - - katalon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Login
    open/coverage.php?coverage=0
    open/adminer/?username=ODBC
    selectname=langlabel=English
    clickAndWaitcss=#lang > input[type="submit"]
    verifyTextPresentLogged as
    clickAndWaitlink=SQL command
    open/adminer/?username=ODBC&sql=DROP+DATABASE+IF+EXISTS+adminer_test
    clickAndWait//input[@value='Execute']
    verifyTextPresentQuery executed OK
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Create database
    open/adminer/?username=ODBC
    clickAndWaitlink=Create database
    typenameadminer_test
    selectcollationlabel=utf8_general_ci
    clickAndWait//input[@value='Save']
    verifyTextPresentDatabase has been created.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Create table
    open/adminer/?username=ODBC&db=adminer_test
    clickAndWaitlink=Create table
    typenameinterprets
    selectEnginelabel=InnoDB
    typefields[1][field]id
    selectfields[1][type]label=int
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]name
    selectfields[1.1][type]label=varchar
    typefields[1.1][length]50
    checkname=comments
    typefields[1.1][comment]Interpret
    typeCommentInterprets
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Create index
    open/adminer/?username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Alter indexes
    selectindexes[2][type]label=PRIMARY
    selectindexes[2][columns][1]label=name
    verifyValuename=indexes[2][name]name
    clickAndWait//input[@value='Save']
    verifyTextPresentMultiple primary key defined
    selectindexes[2][type]label=INDEX
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Partitioning
    open/adminer/?username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Alter table
    selectpartition_bylabel=HASH
    clicklink=Partition by
    typepartitionid
    typepartitions2
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    clickAndWaitlink=Alter table
    selectpartition_bylabel=RANGE
    typepartition_values[]10
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    clickAndWaitlink=Alter table
    selectpartition_bylabel=
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Create table 2
    open/adminer/?username=ODBC&db=adminer_test&table=interprets&lang=en
    clickAndWaitlink=Create table
    typenamealbums
    typefields[1][field]id
    selectfields[1][type]label=int
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]interpret
    selectfields[1.1][type]label=int
    typefields[1.11][field]title
    selectfields[1.11][type]label=varchar
    typefields[1.11][length]50
    checkname=comments
    typefields[1.1][comment]Interpret
    typefields[1.11][comment]Album
    typeCommentAlbums
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Foreign key
    open/adminer/?username=ODBC&db=adminer_test&table=albums
    clickAndWaitlink=Add foreign key
    selectAndWaittablelabel=interprets
    selectsource[0]label=interpret
    clickAndWait//input[@value='Save']
    verifyTextPresentForeign key has been created.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Alter table
    open/adminer/?username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Alter table
    clickadd[2]
    typefields[2.1][field]albums
    selectfields[2.1][type]label=int
    typefields[2.1][length]
    checkname=defaults
    typename=fields[2.1][default]0
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Create trigger
    open/adminer/?username=ODBC&db=adminer_test&trigger=albums
    selectTiminglabel=AFTER
    typeStatementUPDATE interprets SET albums = albums + 1 WHERE id = NEW.interpret
    clickAndWait//input[@value='Save']
    verifyTextPresentTrigger has been created.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Create view
    open/adminer/?username=ODBC&db=adminer_test&view=
    typeselectSELECT albums.id, albums.title, interprets.name -FROM albums -LEFT JOIN interprets ON albums.interpret = interprets.id
    typenamealbums_interprets
    clickAndWait//input[@value='Save']
    verifyTextPresentView has been created.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Insert
    open/adminer/?username=ODBC&db=adminer_test&edit=interprets
    typefields[name]Michael Jackson
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    open/adminer/?username=ODBC&db=adminer_test&edit=albums
    typefields[interpret]1
    typefields[title]Dangerous
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Clone
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clickcheck[]
    clickAndWaitclone
    typefields[title]Black and White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 2 has been inserted.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Explain
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clickAndWaitlink=Edit
    clickAndWait//input[@value='Execute']
    clicklink=Explain
    verifyTextPresentpossible_keys
    - - - - - - - - - - - - - - - - - - - - -
    Reference
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clickAndWaitlink=1
    verifyTextPresentMichael Jackson
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Update
    open/adminer/?username=ODBC&db=adminer_test&edit=albums&where%5Bid%5D=2
    typefields[title]Black or White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem has been updated.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Delete
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    click//input[@name='check[]' and @value='where%5Bid%5D=2']
    waitForChecked//input[@name='check[]' and @value='where%5Bid%5D=2']
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresent1 item has been affected.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Truncate
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clickall
    waitForCheckedall
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresentNo rows.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Privileges
    open/adminer/?username=ODBC&user=
    typeuseradminer_test
    typeobjects[0]adminer_test.*
    clickgrants[0][ALTER]
    clickgrants[0][CREATE]
    click//input[@name='grants[0][CREATE VIEW]']
    clickgrants[0][DELETE]
    clickgrants[0][DROP]
    clickgrants[0][INDEX]
    clickgrants[0][INSERT]
    clickgrants[0][REFERENCES]
    clickgrants[0][SELECT]
    click//input[@name='grants[0][SHOW VIEW]']
    clickgrants[0][UPDATE]
    click//input[@name='grants[0][CREATE TEMPORARY TABLES]']
    click//input[@name='grants[0][LOCK TABLES]']
    click//input[@name='grants[0][CREATE ROUTINE]']
    clickAndWait//input[@value='Save']
    verifyTextPresentUser has been created.
    clickAndWait//div[@id='content']/form/table/tbody/tr[td[1]='adminer_test']/td[3]/a
    chooseOkOnNextConfirmationDrop adminer_test@localhost?
    clickdrop
    verifyTextPresentUser has been dropped.
    - - - - - - - - - - - - - - - -
    Process list
    open/adminer/?username=ODBC&processlist=
    verifyTextPresentSHOW FULL PROCESSLIST
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Export
    open/adminer/?username=ODBC&db=adminer_test&dump=
    clickoutput
    clickformat
    selecttable_stylelabel=DROP+CREATE
    selectdata_stylelabel=INSERT
    clickAndWait//input[@value='Export']
    verifyTextPresentCREATE TABLE `interprets`
    verifyTextPresentCREATE TRIGGER `albums_ai`
    verifyTextPresentINSERT INTO `interprets`
    verifyTextPresentVIEW `albums_interprets`
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Events
    open/adminer/?username=ODBC&db=adminer_test&event=
    typeEVENT_NAMEno_albums
    selectINTERVAL_FIELDlabel=DAY
    typeINTERVAL_VALUE1
    clickON_COMPLETION
    typeEVENT_DEFINITIONDELETE FROM albums WHERE interprets = 0
    clickAndWait//input[@value='Save']
    verifyTextPresentEvent has been created.
    clickAndWaitlink=Alter
    chooseOkOnNextConfirmationDrop no_albums?
    clickdrop
    verifyTextPresentEvent has been dropped.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Procedures
    open/adminer/?username=ODBC&db=adminer_test&procedure=
    clickAndWaitadd[0]
    typefields[1][field]interpret_name
    selectfields[1][type]label=varchar
    typefields[1][length]50
    typefields[1.1][field]album_title
    selectfields[1.1][type]label=varchar
    typefields[1.1][length]50
    typedefinitionBEGIN -SELECT id INTO @interpret FROM interprets WHERE name = interpret_name; -IF @interpret IS NULL THEN - INSERT INTO interprets (name) VALUES (interpret_name); - SET @interpret = LAST_INSERT_ID(); -END IF; -INSERT INTO albums (interpret, title) VALUES (@interpret, album_title); -END
    typenameinsert_album
    clickAndWait//input[@value='Save']
    verifyTextPresentRoutine has been created.
    clickAndWaitlink=insert_album
    typefields[interpret_name]Michael Jackson
    typefields[album_title]Dangerous
    clickAndWait//input[@value='Call']
    assertTextPresentRoutine has been called, 1 row affected.
    clickAndWaitlink=adminer_test
    clickAndWaitlink=Alter
    chooseOkOnNextConfirmationDrop insert_album?
    clickdrop
    verifyTextPresentRoutine has been dropped.
    - - - - - - - - - - - - - - - -
    Variables
    open/adminer/?username=ODBC&variables=
    verifyTextPresentbasedir
    - - - - - - - - - - - - - - - -
    History
    open/adminer/?username=ODBC&sql=
    verifyTextPresentDROP DATABASE IF EXISTS adminer_test
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Warnings
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clickxpath=(//a[contains(text(),'=')])[1]
    typename=where[0][val]1.2.3
    submitid=form
    clicklink=Warnings
    verifyText//div[@id='warnings']/div/table/tbody/tr/td[3]Truncated incorrect DOUBLE value: '1.2.3'
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Editor
    open/editor/example.php?username=admin
    selectname=langlabel=English
    clickAndWaitcss=#lang > input[type="submit"]
    clickAndWaitlink=Interprets
    clickAndWaitlink=New item
    typefields[name]Michael Jackson
    clickAndWait//input[@value='Save']
    clickAndWait//div[@id='content']/form[2]/table/tbody/tr[1]/td[3]/a
    clickAndWaitlink=New item
    typefields[title]Dangerous
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 4 has been inserted.
    clickAndWaitlogout
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SQLite
    open/adminer/sqlite.php
    selectname=auth[driver]label=SQLite 3
    typeid=usernameadmin
    typename=auth[password]
    clickcss=input[type="submit"]
    clicklink=Create database
    typeid=nameadminer_test
    clickcss=input[type="submit"]
    verifyTextPresentPlease use one of the extensions
    typeid=nameadminer_test.sqlite
    clickcss=input[type="submit"]
    verifyTextPresentDatabase has been created.
    clicklink=Create table
    typename=nameinterprets
    clickcss=label.block > input[name="auto_increment_col"]
    typename=fields[1.1][field]name
    selectname=fields[1.1][type]label=text
    clickxpath=(//input[@value='Save'])[2]
    verifyTextPresentTable has been created.
    clicklink=New item
    typename=fields[name]Michael Jackson
    clickcss=input[type="submit"]
    verifyTextPresentItem 1 has been inserted.
    clicklink=Create table
    typename=namealbums
    clickcss=label.block > input[name="auto_increment_col"]
    typename=fields[1.1][field]interpret
    selectname=fields[1.1][on_delete]label=CASCADE
    typename=fields[1.11][field]title
    selectname=fields[1.11][type]label=text
    clickxpath=(//input[@value='Save'])[2]
    verifyTextPresentinterprets(id)
    clicklink=New item
    typename=fields[interpret]1
    typename=fields[title]Dangerous
    clickcss=input[type="submit"]
    clicklink=1
    verifyTextPresentSelect: interprets
    clicklink=adminer_test.sqlite
    clicklink=Alter database
    chooseOkOnNextConfirmationDrop adminer_test.sqlite?
    clickname=drop
    verifyTextPresentDatabase has been dropped.
    clickid=logout
    verifyTextPresentThanks for using Adminer, consider donating.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Logout
    open/adminer/?username=ODBC
    clickAndWaitlogout
    verifyTextPresentLogout successful.
    selectAndWait//select[@name='lang']label=Čeština
    verifyTextPresentPřihlásit se
    open/coverage.php
    - - \ No newline at end of file diff --git a/tests/mariadb.html b/tests/mariadb.html new file mode 100644 index 000000000..8224eba87 --- /dev/null +++ b/tests/mariadb.html @@ -0,0 +1,449 @@ + + + + + + Katalon MariaDB + + + + + + + + + + + + + + + + + + +
    Login
    open/adminer/
    selectname=langlabel=English
    clickAndWaitcss=#lang > input[type="submit"]
    typeid=usernameODBC
    typename=auth[server]localhost:3307
    typename=auth[password]ODBC
    clickAndWait//input[@value='Login']
    verifyTextPresentMariaDB
    clickAndWaitlink=SQL command
    open/adminer/?server=localhost:3307&username=ODBC&sql=DROP+DATABASE+IF+EXISTS+adminer_test
    clickAndWait//input[@value='Execute']
    verifyTextPresentQuery executed OK
    + + + + + + + + + + +
    Create database
    open/adminer/?server=localhost:3307&username=ODBC
    clickAndWaitlink=Create database
    typenameadminer_test
    selectcollationlabel=utf8mb4_general_ci
    clickAndWait//input[@value='Save']
    verifyTextPresentDatabase has been created.
    + + + + + + + + + + + + + + + + + + + + +
    Create table
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test
    clickAndWaitlink=Create table
    typenameinterprets
    selectEnginelabel=InnoDB
    typefields[1][field]id
    selectfields[1][type]label=int
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]name
    selectfields[1.1][type]label=varchar
    typefields[1.1][length]50
    uncheckname=comments
    clickAndWaitname=comments
    typefields[1.1][comment]Interpret
    typeCommentInterprets
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + + + + + +
    Create index
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Alter indexes
    selectindexes[2][type]label=PRIMARY
    selectindexes[2][columns][1]label=name
    verifyValuename=indexes[2][name]name
    clickAndWait//input[@value='Save']
    verifyTextPresentMultiple primary key defined
    selectindexes[2][type]label=INDEX
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    + + + + + + + + + + + + + + + + + + + + + +
    Partitioning
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Alter table
    selectpartition_bylabel=HASH
    clicklink=Partition by
    typepartitionid
    typepartitions2
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    clickAndWaitlink=Alter table
    selectpartition_bylabel=RANGE
    typepartition_values[]10
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    clickAndWaitlink=Alter table
    selectpartition_bylabel=
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    + + + + + + + + + + + + + + + + + + + + + +
    Create table 2
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Create table
    typenamealbums
    typefields[1][field]id
    selectfields[1][type]label=int
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]interpret
    selectfields[1.1][type]label=int
    typefields[1.11][field]title
    selectfields[1.11][type]label=varchar
    typefields[1.11][length]50
    checkname=comments
    typefields[1.1][comment]Interpret
    typefields[1.11][comment]Album
    typeCommentAlbums
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + +
    Foreign key
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&table=albums
    clickAndWaitlink=Add foreign key
    selectAndWaittablelabel=interprets
    selectsource[0]label=interpret
    clickAndWait//input[@value='Save']
    verifyTextPresentForeign key has been created.
    + + + + + + + + + + + + + + + +
    Alter table
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Alter table
    clickadd[2]
    typefields[2.1][field]albums
    selectfields[2.1][type]label=int
    typefields[2.1][length]
    uncheckname=defaults
    clickAndWaitname=defaults
    typename=fields[2.1][default]0
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    + + + + + + + + + +
    Create trigger
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&trigger=albums
    selectTiminglabel=AFTER
    runScriptdocument.querySelector('[name="Statement"]').value = 'UPDATE interprets SET albums = albums + 1 WHERE id = NEW.interpret'
    clickAndWait//input[@value='Save']
    verifyTextPresentTrigger has been created.
    + + + + + + + + + + + + + + + + + + + +
    Check constraints
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&table=albums
    clicklink=Create check
    typename=namealbums_interpret_check
    runScriptdocument.querySelector('[name="clause"]').value = 'interpret > 0'
    click//input[@value='Save']
    verifyTextPresentCheck has been created.
    clickAndWaitlink=New item
    typefields[interpret]0
    click//input[@value='Save']
    verifyTextPresentCONSTRAINT `albums_interpret_check` failed for `adminer_test`.`albums`
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&check=albums&name=albums_interpret_check
    verifyTextPresent`interpret` > 0
    chooseOkOnNextConfirmationDrop albums_interpret_check?
    clickname=drop
    verifyTextPresentCheck has been dropped.
    + + + + + + + + + +
    Create view
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&view=
    runScriptdocument.querySelector('[name="select"]').value = 'SELECT albums.id, albums.title, interprets.name FROM albums LEFT JOIN interprets ON albums.interpret = interprets.id'
    typenamealbums_interprets
    clickAndWait//input[@value='Save']
    verifyTextPresentView has been created.
    + + + + + + + + + + +
    Invalid table
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&table=invalid
    verifyTextPresentNo tables.
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&create=invalid
    verifyTextPresentNo tables.
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&select=invalid
    verifyTextPresentUnable to select the table:
    + + + + + + +
    Schema
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&schema=
    verifyTextPresentPermanent link
    + + + + + + + + + + + + + +
    Insert
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&edit=interprets
    typefields[name]Michael Jackson
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&edit=albums
    typefields[interpret]1
    typefields[title]Dangerous
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    + + + + + + + + + + +
    Clone
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&select=albums
    clickcheck[]
    clickAndWaitclone
    typefields[title]Black and White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 2 has been inserted.
    + + + + + + + + + + + + +
    Select
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&select=albums
    clicklink=Search
    selectname=where[0][col]label=title
    typename=where[0][val]Dangerous
    clicklink=Sort
    selectname=order[0]label=interpret
    clickAndWaitxpath=//input[@value='Select']
    verifyTextPresent1 row
    + + + + + + + + + +
    Explain
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&select=albums
    clickAndWaitlink=Edit
    clickAndWait//input[@value='Execute']
    clicklink=Explain
    verifyTextPresentpossible_keys
    + + + + + + + +
    Reference
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&select=albums
    clickAndWaitlink=1
    verifyTextPresentMichael Jackson
    + + + + + + + + + +
    Search in tables
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test
    typename=queryJackson
    clickAndWaitname=search
    clicklink=interprets
    verifyTextPresentMichael Jackson
    + + + + + + + + +
    Update
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&edit=albums&where%5Bid%5D=2
    typefields[title]Black or White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem has been updated.
    + + + + + + + + + + +
    Delete
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&select=albums
    click//input[@name='check[]' and @value='where%5Bid%5D=2']
    waitForChecked//input[@name='check[]' and @value='where%5Bid%5D=2']
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresent1 item has been affected.
    + + + + + + + + + + +
    Truncate
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&select=albums
    clickall
    waitForCheckedall
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresentNo rows.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Privileges
    open/adminer/?server=localhost:3307&username=ODBC&user=
    typeuseradminer_test
    typeobjects[0]adminer_test.*
    clickgrants[0][ALTER]
    clickgrants[0][CREATE]
    click//input[@name='grants[0][CREATE VIEW]']
    clickgrants[0][DELETE]
    clickgrants[0][DROP]
    clickgrants[0][INDEX]
    clickgrants[0][INSERT]
    clickgrants[0][REFERENCES]
    clickgrants[0][SELECT]
    click//input[@name='grants[0][SHOW VIEW]']
    clickgrants[0][UPDATE]
    click//input[@name='grants[0][CREATE TEMPORARY TABLES]']
    click//input[@name='grants[0][LOCK TABLES]']
    click//input[@name='grants[0][CREATE ROUTINE]']
    clickAndWait//input[@value='Save']
    verifyTextPresentUser has been created.
    clickAndWait//div[@id='content']/form/table/tbody/tr[td[1]='adminer_test']/td[3]/a
    chooseOkOnNextConfirmationDrop adminer_test@localhost?
    clickdrop
    verifyTextPresentUser has been dropped.
    + + + + + + +
    Process list
    open/adminer/?server=localhost:3307&username=ODBC&processlist=
    verifyTextPresentSHOW FULL PROCESSLIST
    + + + + + + + + + + + + + + +
    Export
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&dump=
    clickoutput
    clickformat
    selecttable_stylelabel=DROP+CREATE
    selectdata_stylelabel=INSERT
    clickAndWait//input[@value='Export']
    verifyTextPresentCREATE TABLE `interprets`
    verifyTextPresentCREATE TRIGGER `albums_ai`
    verifyTextPresentINSERT INTO `interprets`
    verifyTextPresentVIEW `albums_interprets`
    + + + + + + + + + + + + + + + + +
    Events
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&event=
    typeEVENT_NAMEno_albums
    selectINTERVAL_FIELDlabel=DAY
    typeINTERVAL_VALUE1
    clickON_COMPLETION
    runScriptdocument.querySelector('[name="EVENT_DEFINITION"]').value = 'DELETE FROM albums WHERE interprets = 0'
    clickAndWait//input[@value='Save']
    verifyTextPresentEvent has been created.
    clickAndWaitlink=Alter
    chooseOkOnNextConfirmationDrop no_albums?
    clickdrop
    verifyTextPresentEvent has been dropped.
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Procedures
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&procedure=
    clickAndWaitadd[0]
    typefields[1][field]interpret_name
    selectfields[1][type]label=varchar
    typefields[1][length]50
    typefields[1.1][field]album_title
    selectfields[1.1][type]label=varchar
    typefields[1.1][length]50
    runScriptdocument.querySelector('[name="definition"]').value = 'BEGIN\nSELECT id INTO @interpret FROM interprets WHERE name = interpret_name;\nIF @interpret IS NULL THEN\n INSERT INTO interprets (name) VALUES (interpret_name);\n SET @interpret = LAST_INSERT_ID();\nEND IF;\nINSERT INTO albums (interpret, title) VALUES (@interpret, album_title);\nEND'
    typenameinsert_album
    clickAndWait//input[@value='Save']
    verifyTextPresentRoutine has been created.
    clickAndWaitlink=insert_album
    typefields[interpret_name]Michael Jackson
    typefields[album_title]Dangerous
    clickAndWait//input[@value='Call']
    assertTextPresentRoutine has been called,
    clickAndWaitlink=adminer_test
    clickAndWaitlink=Alter
    chooseOkOnNextConfirmationDrop insert_album?
    clickdrop
    verifyTextPresentRoutine has been dropped.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Generated columns
    open/adminer/?server=localhost:3307&username=ODBC&db=adminer_test&create=
    typename=namegenerated
    typename=fields[1][field]normal
    typename=fields[1.1][field]virtual
    selectname=fields[1.1][generated]label=VIRTUAL
    typename=fields[1.1][default]normal + 100
    typename=fields[1.11][field]stored
    selectname=fields[1.11][generated]label=STORED
    typename=fields[1.11][default]normal + 200
    clickAndWait//input[@value='Save']
    verifyTextPresent`normal` + 100
    verifyTextPresent`normal` + 200
    clicklink=Alter indexes
    selectname=indexes[1][columns][1]label=virtual
    selectname=indexes[1][columns][11]label=stored
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    clicklink=New item
    verifyElementNotPresentfields[virtual]
    verifyElementNotPresentfields[stored]
    typename=fields[normal]20
    click//input[@value='Save']
    verifyTextPresent120
    verifyTextPresent220
    + + + + + + + + +
    Variables
    open/adminer/?server=localhost:3307&username=ODBC&variables=
    verifyTextPresentbasedir
    open/adminer/?server=localhost:3307&username=ODBC&status=
    verifyTextPresentUptime
    + + + + + + +
    History
    open/adminer/?server=localhost:3307&username=ODBC&sql=
    verifyTextPresentDROP DATABASE IF EXISTS adminer_test
    + + + + + + + + +
    Logout
    open/adminer/?server=localhost:3307&username=ODBC
    clickAndWaitlogout
    verifyTextPresentLogout successful.
    open/coverage.php
    + + + diff --git a/tests/mssql.html b/tests/mssql.html new file mode 100644 index 000000000..02727682c --- /dev/null +++ b/tests/mssql.html @@ -0,0 +1,331 @@ + + + + + + Katalon MS SQL + + + + + + + + + + + + + + +
    Login
    open/adminer/
    selectname=langlabel=English
    clickAndWaitcss=#lang > input[type="submit"]
    selectname=auth[driver]label=MS SQL
    typename=auth[server]
    typeid=usernameODBC
    typename=auth[password]ODBC
    clickAndWait//input[@value='Login']
    + + + + + + + + + + + + + + + + + + + +
    Create table
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo
    clickAndWaitlink=Create table
    typenameinterprets
    typefields[1][field]id
    selectfields[1][type]label=int
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]name
    selectfields[1.1][type]label=varchar
    typefields[1.1][length]50
    uncheckname=comments
    clickAndWaitname=comments
    typefields[1.1][comment]Interpret
    typeCommentInterprets
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + + + + + +
    Create index
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&table=interprets
    clickAndWaitlink=Alter indexes
    selectindexes[2][type]label=PRIMARY
    selectindexes[2][columns][1]label=name
    verifyValuename=indexes[2][name]interprets_name
    clickAndWait//input[@value='Save']
    verifyTextPresentTable 'interprets' already has a primary key defined on it.
    selectindexes[2][type]label=INDEX
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    + + + + + + + + + + + + + + + + + + + +
    Create table 2
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&table=interprets
    clickAndWaitlink=Create table
    typenamealbums
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]interpret
    selectfields[1.1][type]label=int
    typefields[1.11][field]title
    selectfields[1.11][type]label=varchar
    typefields[1.11][length]50
    checkname=comments
    typefields[1.1][comment]Interpret
    typefields[1.11][comment]Album
    typeCommentAlbums
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + +
    Foreign key
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&table=albums
    clickAndWaitlink=Add foreign key
    selectAndWaittablelabel=interprets
    selectsource[0]label=interpret
    clickAndWait//input[@value='Save']
    verifyTextPresentForeign key has been created.
    + + + + + + + + + + + + + + + +
    Alter table
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&table=interprets
    clickAndWaitlink=Alter table
    clickadd[2]
    typefields[3][field]albums
    selectfields[3][type]label=int
    typefields[3][length]
    uncheckname=defaults
    clickAndWaitname=defaults
    typename=fields[3][default]0
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    + + + + + + + + + + + + + + + + + + + +
    Check constraints
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&table=albums
    clicklink=Create check
    typename=namealbums_interpret_check
    runScriptdocument.querySelector('[name="clause"]').value = 'interpret > 0'
    click//input[@value='Save']
    verifyTextPresentCheck has been created.
    clickAndWaitlink=New item
    typefields[interpret]0
    click//input[@value='Save']
    verifyTextPresentstatement conflicted with the CHECK constraint
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&check=albums&name=albums_interpret_check
    verifyTextPresent([interpret]>(0))
    chooseOkOnNextConfirmationDrop albums_interpret_check?
    clickname=drop
    verifyTextPresentCheck has been dropped.
    + + + + + + + + + +
    Create view
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&view=
    runScriptdocument.querySelector('[name="select"]').value = 'SELECT albums.id, albums.title, interprets.name FROM albums LEFT JOIN interprets ON albums.interpret = interprets.id'
    typenamealbums_interprets
    clickAndWait//input[@value='Save']
    verifyTextPresentView has been created.
    + + + + + + + + + + +
    Invalid table
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&table=invalid
    verifyTextPresentNo tables.
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&create=invalid
    verifyTextPresentNo tables.
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&select=invalid
    verifyTextPresentUnable to select the table:
    + + + + + + +
    Schema
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&schema=
    verifyTextPresentPermanent link
    + + + + + + + + + + + + + +
    Insert
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&edit=interprets
    typefields[name]Michael Jackson
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&edit=albums
    typefields[interpret]1
    typefields[title]Dangerous
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 2 has been inserted.
    + + + + + + + + + + +
    Clone
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&select=albums
    clickcheck[]
    clickAndWaitclone
    typefields[title]Black and White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 3 has been inserted.
    + + + + + + + + + + + + +
    Select
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&select=albums
    clicklink=Search
    selectname=where[0][col]label=title
    typename=where[0][val]Dangerous
    clicklink=Sort
    selectname=order[0]label=interpret
    clickAndWaitxpath=//input[@value='Select']
    verifyTextPresent1 row
    + + + + + + + + + +
    Explain
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&select=albums
    clickAndWaitlink=Edit
    clickAndWait//input[@value='Execute']
    clicklink=Explain
    verifyTextPresentClustered Index Scan
    + + + + + + + +
    Reference
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&select=albums
    clickAndWaitlink=1
    verifyTextPresentMichael Jackson
    + + + + + + + + + +
    Search in tables
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo
    typename=queryJackson
    clickAndWaitname=search
    clicklink=interprets
    verifyTextPresentMichael Jackson
    + + + + + + + + +
    Update
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&edit=albums&where%5Bid%5D=2
    typefields[title]Black or White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem has been updated.
    + + + + + + + + + + +
    Delete
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&select=albums
    click//input[@name='check[]' and @value='where%5Bid%5D=2']
    waitForChecked//input[@name='check[]' and @value='where%5Bid%5D=2']
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresent1 item has been affected.
    + + + + + + + + + + +
    Truncate
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&select=albums
    clickall
    waitForCheckedall
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresentNo rows.
    + + + + + + + + + + + + + +
    Export
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&dump=
    clickoutput
    clickformat
    selecttable_stylelabel=DROP+CREATE
    selectdata_stylelabel=INSERT
    clickAndWait//input[@value='Export']
    verifyTextPresentCREATE TABLE [dbo].[interprets]
    verifyTextPresentINSERT INTO [dbo].[interprets]
    verifyTextPresentVIEW [dbo].[albums_interprets]
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Generated columns
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo&create=
    typename=namegenerated
    typename=fields[1][field]normal
    typename=fields[1.1][field]virtual
    selectname=fields[1.1][generated]label=VIRTUAL
    typename=fields[1.1][default]normal + 100
    typename=fields[1.11][field]stored
    selectname=fields[1.11][generated]label=PERSISTED
    typename=fields[1.11][default]normal + 200
    clickAndWait//input[@value='Save']
    verifyTextPresent[normal]+(100)
    verifyTextPresent[normal]+(200)
    clicklink=Alter indexes
    selectname=indexes[1][columns][1]label=virtual
    selectname=indexes[1][columns][11]label=stored
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    clicklink=New item
    verifyElementNotPresentfields[virtual]
    verifyElementNotPresentfields[stored]
    typename=fields[normal]20
    click//input[@value='Save']
    verifyTextPresent120
    verifyTextPresent220
    + + + + + + + + + +
    Scheme
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo
    clicklink=Alter schema
    typenamedbo
    clickAndWait//input[@value='Save']
    verifyTextPresentSchema: dbo
    + + + + + + + + + +
    Drop
    open/adminer/?mssql=&username=ODBC&db=adminer_test&ns=dbo
    clickid=check-all
    chooseOkOnNextConfirmationAre you sure?
    clickname=drop
    verifyTextPresentNo tables.
    + + + + + + + +
    SQL command
    open/adminer/?mssql=&username=ODBC&sql=SELECT+122%2B1
    clickAndWait//input[@value='Execute']
    verifyTextPresent123
    + + + + + + + + +
    Logout
    open/adminer/?mssql=&username=ODBC
    clickAndWaitlogout
    verifyTextPresentLogout successful.
    open/coverage.php
    + + + diff --git a/tests/mysql.html b/tests/mysql.html new file mode 100644 index 000000000..cc25c9da8 --- /dev/null +++ b/tests/mysql.html @@ -0,0 +1,488 @@ + + + + + + Katalon MySQL + + + + + + + + + + + + + + + + + + + + + + + + +
    Login
    open/coverage.php?coverage=0
    open/adminer/
    selectname=langlabel=English
    clickAndWaitcss=#lang > input[type="submit"]
    typeid=usernameODBC
    typename=auth[password]wrong
    clickAndWait//input[@value='Login']
    verifyTextPresentAccess denied for user
    typeid=usernameODBC
    typename=auth[password]ODBC
    clickAndWait//input[@value='Login']
    verifyTextPresentLogged as
    clickAndWaitlink=SQL command
    open/adminer/?username=ODBC&sql=DROP+DATABASE+IF+EXISTS+adminer_test
    clickAndWait//input[@value='Execute']
    verifyTextPresentQuery executed OK
    + + + + + + + + + + +
    Create database
    open/adminer/?username=ODBC
    clickAndWaitlink=Create database
    typenameadminer_test
    selectcollationlabel=utf8mb4_general_ci
    clickAndWait//input[@value='Save']
    verifyTextPresentDatabase has been created.
    + + + + + + + + + + + + + + + + + + + + +
    Create table
    open/adminer/?username=ODBC&db=adminer_test
    clickAndWaitlink=Create table
    typenameinterprets
    selectEnginelabel=InnoDB
    typefields[1][field]id
    selectfields[1][type]label=int
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]name
    selectfields[1.1][type]label=varchar
    typefields[1.1][length]50
    uncheckname=comments
    clickAndWaitname=comments
    typefields[1.1][comment]Interpret
    typeCommentInterprets
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + + + + + +
    Create index
    open/adminer/?username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Alter indexes
    selectindexes[2][type]label=PRIMARY
    selectindexes[2][columns][1]label=name
    verifyValuename=indexes[2][name]name
    clickAndWait//input[@value='Save']
    verifyTextPresentMultiple primary key defined
    selectindexes[2][type]label=INDEX
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    + + + + + + + + + + + + + + + + + + + + + +
    Partitioning
    open/adminer/?username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Alter table
    selectpartition_bylabel=HASH
    clicklink=Partition by
    typepartitionid
    typepartitions2
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    clickAndWaitlink=Alter table
    selectpartition_bylabel=RANGE
    typepartition_values[]10
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    clickAndWaitlink=Alter table
    selectpartition_bylabel=
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    + + + + + + + + + + + + + + + + + + + + + +
    Create table 2
    open/adminer/?username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Create table
    typenamealbums
    typefields[1][field]id
    selectfields[1][type]label=int
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]interpret
    selectfields[1.1][type]label=int
    typefields[1.11][field]title
    selectfields[1.11][type]label=varchar
    typefields[1.11][length]50
    checkname=comments
    typefields[1.1][comment]Interpret
    typefields[1.11][comment]Album
    typeCommentAlbums
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + +
    Foreign key
    open/adminer/?username=ODBC&db=adminer_test&table=albums
    clickAndWaitlink=Add foreign key
    selectAndWaittablelabel=interprets
    selectsource[0]label=interpret
    clickAndWait//input[@value='Save']
    verifyTextPresentForeign key has been created.
    + + + + + + + + + + + + + + + +
    Alter table
    open/adminer/?username=ODBC&db=adminer_test&table=interprets
    clickAndWaitlink=Alter table
    clickadd[2]
    typefields[2.1][field]albums
    selectfields[2.1][type]label=int
    typefields[2.1][length]
    uncheckname=defaults
    clickAndWaitname=defaults
    typename=fields[2.1][default]0
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    + + + + + + + + + +
    Create trigger
    open/adminer/?username=ODBC&db=adminer_test&trigger=albums
    selectTiminglabel=AFTER
    runScriptdocument.querySelector('[name="Statement"]').value = 'UPDATE interprets SET albums = albums + 1 WHERE id = NEW.interpret'
    clickAndWait//input[@value='Save']
    verifyTextPresentTrigger has been created.
    + + + + + + + + + + + + + + + + + + + +
    Check constraints
    open/adminer/?username=ODBC&db=adminer_test&table=albums
    clicklink=Create check
    typename=namealbums_interpret_check
    runScriptdocument.querySelector('[name="clause"]').value = 'interpret > 0'
    click//input[@value='Save']
    verifyTextPresentCheck has been created.
    clickAndWaitlink=New item
    typefields[interpret]0
    click//input[@value='Save']
    verifyTextPresentCheck constraint 'albums_interpret_check' is violated.
    open/adminer/?username=ODBC&db=adminer_test&check=albums&name=albums_interpret_check
    verifyTextPresent(`interpret` > 0)
    chooseOkOnNextConfirmationDrop albums_interpret_check?
    clickname=drop
    verifyTextPresentCheck has been dropped.
    + + + + + + + + + +
    Create view
    open/adminer/?username=ODBC&db=adminer_test&view=
    runScriptdocument.querySelector('[name="select"]').value = 'SELECT albums.id, albums.title, interprets.name FROM albums LEFT JOIN interprets ON albums.interpret = interprets.id'
    typenamealbums_interprets
    clickAndWait//input[@value='Save']
    verifyTextPresentView has been created.
    + + + + + + + + + + +
    Invalid table
    open/adminer/?username=ODBC&db=adminer_test&table=invalid
    verifyTextPresentNo tables.
    open/adminer/?username=ODBC&db=adminer_test&create=invalid
    verifyTextPresentNo tables.
    open/adminer/?username=ODBC&db=adminer_test&select=invalid
    verifyTextPresentUnable to select the table:
    + + + + + + +
    Schema
    open/adminer/?username=ODBC&db=adminer_test&schema=
    verifyTextPresentPermanent link
    + + + + + + + + + + + + + +
    Insert
    open/adminer/?username=ODBC&db=adminer_test&edit=interprets
    typefields[name]Michael Jackson
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    open/adminer/?username=ODBC&db=adminer_test&edit=albums
    typefields[interpret]1
    typefields[title]Dangerous
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    + + + + + + + + + + +
    Clone
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clickcheck[]
    clickAndWaitclone
    typefields[title]Black and White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 2 has been inserted.
    + + + + + + + + + + + + +
    Select
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clicklink=Search
    selectname=where[0][col]label=title
    typename=where[0][val]Dangerous
    clicklink=Sort
    selectname=order[0]label=interpret
    clickAndWaitxpath=//input[@value='Select']
    verifyTextPresent1 row
    + + + + + + + + + +
    Explain
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clickAndWaitlink=Edit
    clickAndWait//input[@value='Execute']
    clicklink=Explain
    verifyTextPresentpossible_keys
    + + + + + + + +
    Reference
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clickAndWaitlink=1
    verifyTextPresentMichael Jackson
    + + + + + + + + + +
    Search in tables
    open/adminer/?username=ODBC&db=adminer_test
    typename=queryJackson
    clickAndWaitname=search
    clicklink=interprets
    verifyTextPresentMichael Jackson
    + + + + + + + + +
    Update
    open/adminer/?username=ODBC&db=adminer_test&edit=albums&where%5Bid%5D=2
    typefields[title]Black or White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem has been updated.
    + + + + + + + + + + +
    Delete
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    click//input[@name='check[]' and @value='where%5Bid%5D=2']
    waitForChecked//input[@name='check[]' and @value='where%5Bid%5D=2']
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresent1 item has been affected.
    + + + + + + + + + + +
    Truncate
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    clickall
    waitForCheckedall
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresentNo rows.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Privileges
    open/adminer/?username=ODBC&user=
    typeuseradminer_test
    typeobjects[0]adminer_test.*
    clickgrants[0][ALTER]
    clickgrants[0][CREATE]
    click//input[@name='grants[0][CREATE VIEW]']
    clickgrants[0][DELETE]
    clickgrants[0][DROP]
    clickgrants[0][INDEX]
    clickgrants[0][INSERT]
    clickgrants[0][REFERENCES]
    clickgrants[0][SELECT]
    click//input[@name='grants[0][SHOW VIEW]']
    clickgrants[0][UPDATE]
    click//input[@name='grants[0][CREATE TEMPORARY TABLES]']
    click//input[@name='grants[0][LOCK TABLES]']
    click//input[@name='grants[0][CREATE ROUTINE]']
    clickAndWait//input[@value='Save']
    verifyTextPresentUser has been created.
    clickAndWait//div[@id='content']/form/table/tbody/tr[td[1]='adminer_test']/td[3]/a
    chooseOkOnNextConfirmationDrop adminer_test@localhost?
    clickdrop
    verifyTextPresentUser has been dropped.
    + + + + + + +
    Process list
    open/adminer/?username=ODBC&processlist=
    verifyTextPresentSHOW FULL PROCESSLIST
    + + + + + + + + + + + + + + +
    Export
    open/adminer/?username=ODBC&db=adminer_test&dump=
    clickoutput
    clickformat
    selecttable_stylelabel=DROP+CREATE
    selectdata_stylelabel=INSERT
    clickAndWait//input[@value='Export']
    verifyTextPresentCREATE TABLE `interprets`
    verifyTextPresentCREATE TRIGGER `albums_ai`
    verifyTextPresentINSERT INTO `interprets`
    verifyTextPresentVIEW `albums_interprets`
    + + + + + + + + + + + + + + + + +
    Events
    open/adminer/?username=ODBC&db=adminer_test&event=
    typeEVENT_NAMEno_albums
    selectINTERVAL_FIELDlabel=DAY
    typeINTERVAL_VALUE1
    clickON_COMPLETION
    runScriptdocument.querySelector('[name="EVENT_DEFINITION"]').value = 'DELETE FROM albums WHERE interprets = 0'
    clickAndWait//input[@value='Save']
    verifyTextPresentEvent has been created.
    clickAndWaitlink=Alter
    chooseOkOnNextConfirmationDrop no_albums?
    clickdrop
    verifyTextPresentEvent has been dropped.
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Procedures
    open/adminer/?username=ODBC&db=adminer_test&procedure=
    clickAndWaitadd[0]
    typefields[1][field]interpret_name
    selectfields[1][type]label=varchar
    typefields[1][length]50
    typefields[1.1][field]album_title
    selectfields[1.1][type]label=varchar
    typefields[1.1][length]50
    runScriptdocument.querySelector('[name="definition"]').value = 'BEGIN\nSELECT id INTO @interpret FROM interprets WHERE name = interpret_name;\nIF @interpret IS NULL THEN\n INSERT INTO interprets (name) VALUES (interpret_name);\n SET @interpret = LAST_INSERT_ID();\nEND IF;\nINSERT INTO albums (interpret, title) VALUES (@interpret, album_title);\nEND'
    typenameinsert_album
    clickAndWait//input[@value='Save']
    verifyTextPresentRoutine has been created.
    clickAndWaitlink=insert_album
    typefields[interpret_name]Michael Jackson
    typefields[album_title]Dangerous
    clickAndWait//input[@value='Call']
    assertTextPresentRoutine has been called,
    clickAndWaitlink=adminer_test
    clickAndWaitlink=Alter
    chooseOkOnNextConfirmationDrop insert_album?
    clickdrop
    verifyTextPresentRoutine has been dropped.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Generated columns
    open/adminer/?username=ODBC&db=adminer_test&create=
    typename=namegenerated
    typename=fields[1][field]normal
    typename=fields[1.1][field]virtual
    selectname=fields[1.1][generated]label=VIRTUAL
    typename=fields[1.1][default]normal + 100
    typename=fields[1.11][field]stored
    selectname=fields[1.11][generated]label=STORED
    typename=fields[1.11][default]normal + 200
    clickAndWait//input[@value='Save']
    verifyTextPresent`normal` + 100
    verifyTextPresent`normal` + 200
    clicklink=Alter indexes
    selectname=indexes[1][columns][1]label=virtual
    selectname=indexes[1][columns][11]label=stored
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    clicklink=New item
    verifyElementNotPresentfields[virtual]
    verifyElementNotPresentfields[stored]
    typename=fields[normal]20
    click//input[@value='Save']
    verifyTextPresent120
    verifyTextPresent220
    + + + + + + + + +
    Variables
    open/adminer/?username=ODBC&variables=
    verifyTextPresentbasedir
    open/adminer/?username=ODBC&status=
    verifyTextPresentUptime
    + + + + + + +
    History
    open/adminer/?username=ODBC&sql=
    verifyTextPresentDROP DATABASE IF EXISTS adminer_test
    + + + + + + + + + + +
    Warnings
    open/adminer/?username=ODBC&db=adminer_test&select=albums
    click//th[@id='th[interpret]']/span/a[2]
    typename=where[0][val]1.2.3
    submitid=form
    clicklink=Warnings
    verifyText//div[@id='warnings']/div/table/tbody/tr/td[3]Truncated incorrect DOUBLE value: '1.2.3'
    + + + + + + + + + + + + + + + + + + + +
    Editor
    open/editor/example.php
    selectname=langlabel=English
    clickAndWaitcss=#lang > input[type="submit"]
    typename=auth[username]admin
    click//input[@value='Login']
    clickAndWaitlink=Interprets
    clickAndWaitlink=New item
    typefields[name]Michael Jackson
    clickAndWait//input[@value='Save']
    clickAndWaitlink=Albums
    clickAndWaitlink=New item
    typefields[title]Dangerous
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 4 has been inserted.
    clickAndWaitlogout
    + + + + + + + + + + +
    Logout
    open/adminer/?username=ODBC
    clickAndWaitlogout
    verifyTextPresentLogout successful.
    selectAndWait//select[@name='lang']label=Čeština
    verifyTextPresentPřihlásit se
    open/coverage.php
    + + + diff --git a/tests/pgsql.html b/tests/pgsql.html new file mode 100644 index 000000000..b724787af --- /dev/null +++ b/tests/pgsql.html @@ -0,0 +1,470 @@ + + + + + + Katalon PostgreSQL + + + + + + + + + + + + + +
    Login
    open/adminer/
    selectname=langlabel=English
    clickAndWaitcss=#lang > input[type="submit"]
    selectname=auth[driver]label=PostgreSQL
    typeid=usernameODBC
    typename=auth[password]ODBC
    clickAndWait//input[@value='Login']
    + + + + + + + + + + + + + + + + + + + + + + +
    Create table
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public
    clickAndWaitlink=Create table
    typenameinterprets
    typefields[1][field]id
    selectfields[1][type]label=integer
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]name
    selectfields[1.1][type]label=character varying
    typefields[1.1][length]50
    typefields[1.11][field]surname
    selectfields[1.11][type]label=character varying
    typefields[1.11][length]50
    uncheckname=comments
    clickAndWaitname=comments
    typefields[1.1][comment]Interpret
    typeCommentInterprets
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + + + + + + + + + + + + +
    Create index
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&table=interprets
    clickAndWaitlink=Alter indexes
    selectindexes[2][type]label=PRIMARY
    selectindexes[2][columns][1]label=name
    verifyValuename=indexes[2][name]interprets_name
    clickAndWait//input[@value='Save']
    verifyTextPresentmultiple primary keys for table "interprets" are not allowed
    selectindexes[2][type]label=INDEX
    click//input[@name='options']
    selectindexes[3][type]label=INDEX
    selectindexes[3][columns][1]label=surname
    selectindexes[3][algorithm]label=hash
    typeindexes[3][partial]surname IS NOT NULL
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    verifyTextPresentINDEX (hash)
    verifyTextPresentWHERE surname IS NOT NULL
    + + + + + + + + + + + + + + + + + + + +
    Create table 2
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&table=interprets
    clickAndWaitlink=Create table
    typenamealbums
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]interpret
    selectfields[1.1][type]label=integer
    typefields[1.11][field]title
    selectfields[1.11][type]label=character varying
    typefields[1.11][length]50
    checkname=comments
    typefields[1.1][comment]Interpret
    typefields[1.11][comment]Album
    typeCommentAlbums
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + +
    Foreign key
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&table=albums
    clickAndWaitlink=Add foreign key
    selectAndWaittablelabel=interprets
    selectsource[0]label=interpret
    clickAndWait//input[@value='Save']
    verifyTextPresentForeign key has been created.
    + + + + + + + + + + + + + + + +
    Alter table
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&table=interprets
    clickAndWaitlink=Alter table
    clickadd[3]
    typefields[4][field]albums
    selectfields[4][type]label=integer
    typefields[4][length]
    uncheckname=defaults
    clickAndWaitname=defaults
    typename=fields[4][default]0
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    + + + + + + + + + + + + + + + + + + + +
    Check constraints
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&table=albums
    clicklink=Create check
    typename=namealbums_interpret_check
    runScriptdocument.querySelector('[name="clause"]').value = 'interpret > 0'
    click//input[@value='Save']
    verifyTextPresentCheck has been created.
    clickAndWaitlink=New item
    typefields[interpret]0
    click//input[@value='Save']
    verifyTextPresentviolates check constraint
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&check=albums&name=albums_interpret_check
    verifyTextPresent(interpret > 0)
    chooseOkOnNextConfirmationDrop albums_interpret_check?
    clickname=drop
    verifyTextPresentCheck has been dropped.
    + + + + + + + + + +
    Create view
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&view=
    runScriptdocument.querySelector('[name="select"]').value = 'SELECT albums.id, albums.title, interprets.name FROM albums LEFT JOIN interprets ON albums.interpret = interprets.id'
    typenamealbums_interprets
    clickAndWait//input[@value='Save']
    verifyTextPresentView has been created.
    + + + + + + + + + + +
    Materialized view
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&view=
    runScriptdocument.querySelector('[name="select"]').value = 'SELECT albums.id, albums.title, interprets.name FROM albums LEFT JOIN interprets ON albums.interpret = interprets.id'
    typenamematerialized_view
    clickmaterialized
    clickAndWait//input[@value='Save']
    verifyTextPresentMaterialized view
    + + + + + + + + + + +
    Invalid table
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&table=invalid
    verifyTextPresentNo tables.
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&create=invalid
    verifyTextPresentNo tables.
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&select=invalid
    verifyTextPresentUnable to select the table:
    + + + + + + +
    Schema
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&schema=
    verifyTextPresentPermanent link
    + + + + + + + + + + + + + +
    Insert
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&edit=interprets
    typefields[name]Michael Jackson
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&edit=albums
    typefields[interpret]1
    typefields[title]Dangerous
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 2 has been inserted.
    + + + + + + + + + + +
    Clone
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&select=albums
    clickcheck[]
    clickAndWaitclone
    typefields[title]Black and White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 3 has been inserted.
    + + + + + + + + + + + + +
    Select
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&select=albums
    clicklink=Search
    selectname=where[0][col]label=title
    typename=where[0][val]Dangerous
    clicklink=Sort
    selectname=order[0]label=interpret
    clickAndWaitxpath=//input[@value='Select']
    verifyTextPresent1 row
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Enum
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public
    clicklink=Create type
    typename=namealive
    runScriptdocument.querySelector('[name="as"]').value = "AS ENUM('alive', 'deceased')"
    clickAndWait//input[@value='Save']
    clicklink=interprets
    clicklink=Alter table
    clickname=add[4]
    typename=fields[5][field]alive
    selectname=fields[5][type]label=alive
    clickname=fields[5][null]
    clickAndWait//input[@value='Save']
    clicklink=alive
    verifyTextPresent'alive', 'deceased'
    chooseOkOnNextConfirmationDrop alive?
    clickAndWait//input[@value='Drop']
    verifyTextPresentcannot drop type
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&edit=interprets&where%5Bid%5D=1
    click//input[@value='val-deceased']
    clickAndWait//input[@value='Save']
    verifyTextPresentdeceased
    + + + + + + + + + +
    Explain
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&select=albums
    clickAndWaitlink=Edit
    clickAndWait//input[@value='Execute']
    clicklink=Explain
    verifyTextPresentSeq Scan
    + + + + + + + +
    Reference
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&select=albums
    clickAndWaitlink=1
    verifyTextPresentMichael Jackson
    + + + + + + + + + +
    Search in tables
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public
    typename=queryJackson
    clickAndWaitname=search
    clicklink=interprets
    verifyTextPresentMichael Jackson
    + + + + + + + + +
    Update
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&edit=albums&where%5Bid%5D=2
    typefields[title]Black or White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem has been updated.
    + + + + + + + + + + +
    Delete
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&select=albums
    click//input[@name='check[]' and @value='where%5Bid%5D=2']
    waitForChecked//input[@name='check[]' and @value='where%5Bid%5D=2']
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresent1 item has been affected.
    + + + + + + + + + + +
    Truncate
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&select=albums
    clickall
    waitForCheckedall
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresentNo rows.
    + + + + + + +
    Process list
    open/adminer/?pgsql=&username=ODBC&processlist=
    verifyTextPresentpg_stat_activity
    + + + + + + + + + + + + + +
    Export
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&dump=
    clickoutput
    clickformat
    selecttable_stylelabel=DROP+CREATE
    selectdata_stylelabel=INSERT
    clickAndWait//input[@value='Export']
    verifyTextPresentCREATE TABLE "public"."interprets"
    verifyTextPresentINSERT INTO "interprets"
    verifyTextPresentVIEW "albums_interprets"
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Procedures
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&procedure=
    clickAndWaitadd[0]
    typefields[1][field]interpret_name
    selectfields[1][type]label=character varying
    typefields[1][length]50
    typefields[1.1][field]album_title
    selectfields[1.1][type]label=character varying
    typefields[1.1][length]50
    runScriptdocument.querySelector('[name="definition"]').value = 'SELECT id FROM interprets;'
    typenameinsert_album
    selectname=languagelabel=sql
    clickAndWait//input[@value='Save']
    verifyTextPresentRoutine has been created.
    clickAndWaitlink=insert_album
    typefields[interpret_name]Michael Jackson
    typefields[album_title]Dangerous
    clickAndWait//input[@value='Call']
    assertTextPresentRoutine has been called,
    clickAndWaitlink=public
    clickAndWaitlink=Alter
    chooseOkOnNextConfirmationDrop insert_album?
    clickdrop
    verifyTextPresentRoutine has been dropped.
    + + + + + + + + + + + + + + + + + + + + + +
    Generated columns
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&create=
    typename=namegenerated
    typename=fields[1][field]normal
    typename=fields[1.1][field]stored
    selectname=fields[1.1][generated]label=STORED
    typename=fields[1.1][default]normal + 200
    clickAndWait//input[@value='Save']
    verifyTextPresentnormal + 200
    clicklink=Alter indexes
    selectname=indexes[1][columns][1]label=stored
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    clicklink=New item
    verifyElementNotPresentfields[stored]
    typename=fields[normal]20
    click//input[@value='Save']
    verifyTextPresent220
    + + + + + + + + + +
    Sequences
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public
    clicklink=albums_id_seq
    typenamealbums_id_seq2
    clickAndWait//input[@value='Save']
    verifyTextPresentSequence has been altered.
    + + + + + + + + + +
    Scheme
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public
    clicklink=Alter schema
    typenamepublic
    clickAndWait//input[@value='Save']
    verifyTextPresentSchema: public
    + + + + + + + + + +
    Drop
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public
    clickid=check-all
    chooseOkOnNextConfirmationAre you sure?
    clickname=drop
    verifyTextPresentNo tables.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Partitioning
    open/adminer/?pgsql=&username=ODBC&db=adminer_test&ns=public&create=
    typename=namerange
    click//input[@name='auto_increment_col' and @value='1']
    clicklink=Partition by
    selectname=partition_bylabel=RANGE
    typename=partitionid
    typename=partition_names[]old
    typename=partition_values[]10
    typexpath=//table[@id='partition-table']/tr/td/inputnew
    typexpath=//table[@id='partition-table']/tr/td[2]/inputMAXVALUE
    click//input[@value='Save']
    verifyTextPresentPARTITION BY RANGE(id)
    verifyTextPresent"range_old" PARTITION OF "range" FOR VALUES FROM (MINVALUE) TO (10)
    clicklink=Create table
    typename=namelist
    click//input[@name='auto_increment_col' and @value='1']
    clicklink=Partition by
    selectname=partition_bylabel=LIST
    typename=partitionid
    typename=partition_names[]odd
    typename=partition_values[]1,3,5
    clickxpath=//input[@value='Save']
    verifyTextPresentPARTITION BY LIST(id)
    verifyTextPresent"list_odd" PARTITION OF "list" FOR VALUES IN (1,3,5)
    clicklink=Create table
    typename=namehash
    click//input[@name='auto_increment_col' and @value='1']
    clicklink=Partition by
    selectname=partition_bylabel=HASH
    typename=partitionid
    typename=partitions4
    clickxpath=//input[@value='Save']
    verifyTextPresentPARTITION BY HASH(id)
    verifyTextPresent"hash_0" PARTITION OF "hash" FOR VALUES WITH (MODULUS 4, REMAINDER 0)
    clicklink=hash_0
    verifyTextPresentInherits from
    clicklink=public
    click//input[@name='tables[]' and @value='hash']
    click//input[@name='tables[]' and @value='list']
    click//input[@name='tables[]' and @value='range']
    chooseOkOnNextConfirmationAre you sure?
    clickname=drop
    verifyTextPresentNo tables.
    + + + + + + +
    Variables
    open/adminer/?pgsql=&username=ODBC&variables=
    verifyTextPresentautovacuum
    + + + + + + + +
    SQL command
    open/adminer/?pgsql=&username=ODBC&sql=SELECT+122%2B1
    clickAndWait//input[@value='Execute']
    verifyTextPresent123
    + + + + + + + + +
    Logout
    open/adminer/?pgsql=&username=ODBC
    clickAndWaitlogout
    verifyTextPresentLogout successful.
    open/coverage.php
    + + + diff --git a/tests/screenshots.html b/tests/screenshots.html new file mode 100644 index 000000000..2230ad967 --- /dev/null +++ b/tests/screenshots.html @@ -0,0 +1,65 @@ + + + + + + Katalon screenshots + + + + + + + + + + + + + + + + + + + +
    Login
    open/adminer/
    selectname=langlabel=English
    clickAndWaitcss=#lang > input[type="submit"]
    typename=auth[password]
    checkname=auth[permanent]
    typeid=usernameODBC
    captureEntirePageScreenshotauth
    typename=auth[password]ODBC
    typename=auth[db]cds
    clickAndWait//input[@value='Login']
    captureEntirePageScreenshotdb
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Screenshots
    clickAndWaitlink=albums
    captureEntirePageScreenshottable
    clickAndWaitxpath=//big
    captureEntirePageScreenshotdark
    clickAndWaitxpath=//big
    clickAndWaitlink=Alter table
    checkname=comments
    clickAndWaitname=comments
    checkname=defaults
    clickAndWaitname=defaults
    captureEntirePageScreenshotcreate
    open/adminer/?username=ODBC&db=cds&select=songs
    captureEntirePageScreenshotselect
    open/adminer/?username=ODBC&db=cds&edit=songs&where[id]=14
    clickname=fields[title]
    captureEntirePageScreenshotedit
    open/adminer/?username=ODBC&dbsize=1
    captureEntirePageScreenshotdatabase
    open/adminer/?username=ODBC&db=cds&schema=albums:1x8_interprets:8x6_songs:6x-1
    captureEntirePageScreenshotschema
    open/adminer/?username=ODBC&db=cds&dump=
    captureEntirePageScreenshotdump
    open/adminer/?username=ODBC&db=cds&sql=SELECT+%2A%0AFROM+%60albums%60%0ALIMIT+50
    clickAndWait//input[@value='Execute']
    captureEntirePageScreenshotsql
    + + + diff --git a/tests/screenshots.php b/tests/screenshots.php new file mode 100755 index 000000000..cf56eb4be --- /dev/null +++ b/tests/screenshots.php @@ -0,0 +1,22 @@ +#!/usr/bin/env php + array(1106, 412), + 'dark' => array(816, 750), + 'database' => array(896, 666), + 'db' => array(1258, 752), + 'dump' => array(784, 450), + 'edit' => array(1006, 336), + 'login' => array(628, 326), + 'select' => array(924, 810), + 'schema' => array(690, 406), + 'sql' => array(870, 788), + 'table' => array(816, 750), + ) as $filename => list($w, $h) +) { + $im = imagecreatefrompng("screenshots/$filename.png"); + $im2 = imagecreatetruecolor($w, $h); + imagecopy($im2, $im, 0, 0, 0, 0, $w, $h); + imagepng($im2, "cropped/$filename.png"); +} diff --git a/tests/sqlite.html b/tests/sqlite.html new file mode 100644 index 000000000..eb9ef7f4e --- /dev/null +++ b/tests/sqlite.html @@ -0,0 +1,318 @@ + + + + + + Katalon SQLite + + + + + + + + + + + + + + + + + + + + + +
    Login
    open/adminer/sqlite.php
    selectname=langlabel=English
    clickAndWaitcss=#lang > input[type="submit"]
    selectname=auth[driver]label=SQLite
    typeid=usernameODBC
    typename=auth[password]YOUR_PASSWORD_HERE
    click//input[@value='Login']
    verifyTextPresentAdminerLoginPasswordLess
    clicklink=Create database
    typename=nameadminer_test
    clickcss=input[type="submit"]
    verifyTextPresentPlease use one of the extensions
    typename=nameadminer_test.sqlite
    clickcss=input[type="submit"]
    verifyTextPresentDatabase has been created.
    + + + + + + + + + + + + + + + +
    Create table
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite
    clickAndWaitlink=Create table
    typenameinterprets
    typefields[1][field]id
    selectfields[1][type]label=integer
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]name
    selectfields[1.1][type]label=text
    typefields[1.1][length]50
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + + + + + +
    Create index
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&table=interprets
    clickAndWaitlink=Alter indexes
    selectindexes[2][type]label=PRIMARY
    selectindexes[2][columns][1]label=name
    verifyValuename=indexes[2][name]interprets_name
    clickAndWait//input[@value='Save']
    verifyTextPresenthas more than one primary key
    selectindexes[2][type]label=INDEX
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    + + + + + + + + + + + + + + + + + +
    Create table 2
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&table=interprets
    clickAndWaitlink=Create table
    typenamealbums
    typefields[1][field]id
    selectfields[1][type]label=integer
    click//input[@name='auto_increment_col' and @value='1']
    typefields[1.1][field]interpret
    selectfields[1.1][type]label=integer
    typefields[1.11][field]title
    selectfields[1.11][type]label=text
    typefields[1.11][length]50
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been created.
    + + + + + + + + + + +
    Foreign key
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&table=albums
    clickAndWaitlink=Add foreign key
    selectAndWaittablelabel=interprets
    selectsource[0]label=interpret
    clickAndWait//input[@value='Save']
    verifyTextPresentForeign key has been created.
    + + + + + + + + + + + + + + + +
    Alter table
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&table=interprets
    clickAndWaitlink=Alter table
    clickadd[2]
    typefields[2.1][field]albums
    selectfields[2.1][type]label=integer
    typefields[2.1][length]
    uncheckname=defaults
    clickAndWaitname=defaults
    typename=fields[2.1][default]0
    clickAndWait//input[@value='Save']
    verifyTextPresentTable has been altered.
    + + + + + + + + + +
    Create trigger
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&trigger=albums
    selectTiminglabel=AFTER
    runScriptdocument.querySelector('[name="Statement"]').value = 'BEGIN\nUPDATE interprets SET albums = albums + 1 WHERE id = NEW.interpret;\nEND'
    clickAndWait//input[@value='Save']
    verifyTextPresentTrigger has been created.
    + + + + + + + + + + + + + + + + + + +
    Check constraints
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&table=albums
    clicklink=Create check
    runScriptdocument.querySelector('[name="clause"]').value = 'interpret > 0'
    click//input[@value='Save']
    verifyTextPresentCheck has been created.
    clickAndWaitlink=New item
    typefields[interpret]0
    click//input[@value='Save']
    verifyTextPresentCHECK constraint failed
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&check=albums&name=interpret+%3E+0
    verifyTextPresentinterpret > 0
    chooseOkOnNextConfirmationDrop interpret > 0?
    clickname=drop
    verifyTextPresentCheck has been dropped.
    + + + + + + + + + +
    Create view
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&view=
    runScriptdocument.querySelector('[name="select"]').value = 'SELECT albums.id, albums.title, interprets.name FROM albums LEFT JOIN interprets ON albums.interpret = interprets.id'
    typenamealbums_interprets
    clickAndWait//input[@value='Save']
    verifyTextPresentView has been created.
    + + + + + + + + + + +
    Invalid table
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&table=invalid
    verifyTextPresentNo tables.
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&create=invalid
    verifyTextPresentNo tables.
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&select=invalid
    verifyTextPresentUnable to select the table:
    + + + + + + +
    Schema
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&schema=
    verifyTextPresentPermanent link
    + + + + + + + + + + + + + +
    Insert
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&edit=interprets
    typefields[name]Michael Jackson
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&edit=albums
    typefields[interpret]1
    typefields[title]Dangerous
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 1 has been inserted.
    + + + + + + + + + + +
    Clone
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&select=albums
    clickcheck[]
    clickAndWaitclone
    typefields[title]Black and White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem 2 has been inserted.
    + + + + + + + + + + + + +
    Select
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&select=albums
    clicklink=Search
    selectname=where[0][col]label=title
    typename=where[0][val]Dangerous
    clicklink=Sort
    selectname=order[0]label=interpret
    clickAndWaitxpath=//input[@value='Select']
    verifyTextPresent1 row
    + + + + + + + + + +
    Explain
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&select=albums
    clickAndWaitlink=Edit
    clickAndWait//input[@value='Execute']
    clicklink=Explain
    verifyTextPresentSCAN albums
    + + + + + + + +
    Reference
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&select=albums
    clickAndWaitlink=1
    verifyTextPresentMichael Jackson
    + + + + + + + + + +
    Search in tables
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite
    typename=queryJackson
    clickAndWaitname=search
    clicklink=interprets
    verifyTextPresentMichael Jackson
    + + + + + + + + +
    Update
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&edit=albums&where%5Bid%5D=2
    typefields[title]Black or White
    clickAndWait//input[@value='Save']
    verifyTextPresentItem has been updated.
    + + + + + + + + + + +
    Delete
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&select=albums
    click//input[@name='check[]' and @value='where%5Bid%5D=2']
    waitForChecked//input[@name='check[]' and @value='where%5Bid%5D=2']
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresent1 item has been affected.
    + + + + + + + + + + +
    Truncate
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&select=albums
    clickall
    waitForCheckedall
    chooseOkOnNextConfirmationAre you sure?
    clickdelete
    verifyTextPresentNo rows.
    + + + + + + + + + + + + + + +
    Export
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&dump=
    clickoutput
    clickformat
    selecttable_stylelabel=DROP+CREATE
    selectdata_stylelabel=INSERT
    clickAndWait//input[@value='Export']
    verifyTextPresentCREATE TABLE "interprets"
    verifyTextPresentCREATE TRIGGER "albums_ai"
    verifyTextPresentINSERT INTO "interprets"
    verifyTextPresentVIEW "albums_interprets"
    + + + + + + + + + + + + + + + + + + + + + +
    Generated columns
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&create=
    typename=namegenerated
    typename=fields[1][field]normal
    typename=fields[1.1][field]stored
    selectname=fields[1.1][generated]label=STORED
    typename=fields[1.1][default]normal + 200
    clickAndWait//input[@value='Save']
    verifyTextPresentnormal + 200
    clicklink=Alter indexes
    selectname=indexes[1][columns][1]label=stored
    clickAndWait//input[@value='Save']
    verifyTextPresentIndexes have been altered.
    clicklink=New item
    verifyElementNotPresentfields[stored]
    typename=fields[normal]20
    click//input[@value='Save']
    verifyTextPresent220
    + + + + + + + + +
    Variables
    open/adminer/sqlite.php?sqlite=&username=ODBC&variables=
    verifyTextPresentintegrity_check
    open/adminer/sqlite.php?sqlite=&username=ODBC&status=
    verifyTextPresentMAX_COLUMN
    + + + + + + + + + + +
    Drop
    open/adminer/sqlite.php?sqlite=&username=ODBC&db=adminer_test.sqlite&database=
    chooseOkOnNextConfirmationDrop adminer_test.sqlite?
    clickname=drop
    verifyTextPresentDatabase has been dropped.
    clickid=logout
    verifyTextPresentThanks for using Adminer, consider donating.
    + + + diff --git a/todo.txt b/todo.txt index bf6b99864..db7c2a0fd 100644 --- a/todo.txt +++ b/todo.txt @@ -7,12 +7,15 @@ Export by GET parameters Draggable columns in alter table (thanks to Michal Manak)