Repository: vrana/adminer Branch: master Commit: 5754b32693fe Files: 261 Total size: 2.1 MB Directory structure: gitextract_k5k2lsdc/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ └── bug_report.md │ └── workflows/ │ └── ci.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── adminer/ │ ├── call.inc.php │ ├── check.inc.php │ ├── create.inc.php │ ├── database.inc.php │ ├── db.inc.php │ ├── designs.php │ ├── download.inc.php │ ├── drivers/ │ │ ├── mssql.inc.php │ │ ├── mysql.inc.php │ │ ├── oracle.inc.php │ │ ├── pgsql.inc.php │ │ └── sqlite.inc.php │ ├── dump.inc.php │ ├── edit.inc.php │ ├── elastic.php │ ├── event.inc.php │ ├── file.inc.php │ ├── foreign.inc.php │ ├── include/ │ │ ├── adminer.inc.php │ │ ├── auth.inc.php │ │ ├── bootstrap.inc.php │ │ ├── connect.inc.php │ │ ├── coverage.inc.php │ │ ├── db.inc.php │ │ ├── design.inc.php │ │ ├── driver.inc.php │ │ ├── editing.inc.php │ │ ├── errors.inc.php │ │ ├── functions.inc.php │ │ ├── html.inc.php │ │ ├── lang.inc.php │ │ ├── pdo.inc.php │ │ ├── plugin.inc.php │ │ ├── plugins.inc.php │ │ ├── tmpfile.inc.php │ │ ├── version.inc.php │ │ └── xxtea.inc.php │ ├── index.php │ ├── indexes.inc.php │ ├── lang/ │ │ ├── ar.inc.php │ │ ├── bg.inc.php │ │ ├── bn.inc.php │ │ ├── bs.inc.php │ │ ├── ca.inc.php │ │ ├── cs.inc.php │ │ ├── da.inc.php │ │ ├── de.inc.php │ │ ├── el.inc.php │ │ ├── en.inc.php │ │ ├── es.inc.php │ │ ├── et.inc.php │ │ ├── fa.inc.php │ │ ├── fi.inc.php │ │ ├── fr.inc.php │ │ ├── gl.inc.php │ │ ├── he.inc.php │ │ ├── hi.inc.php │ │ ├── hr.inc.php │ │ ├── hu.inc.php │ │ ├── id.inc.php │ │ ├── it.inc.php │ │ ├── ja.inc.php │ │ ├── ka.inc.php │ │ ├── ko.inc.php │ │ ├── lt.inc.php │ │ ├── lv.inc.php │ │ ├── ms.inc.php │ │ ├── nl.inc.php │ │ ├── no.inc.php │ │ ├── pl.inc.php │ │ ├── pt-br.inc.php │ │ ├── pt.inc.php │ │ ├── ro.inc.php │ │ ├── ru.inc.php │ │ ├── sk.inc.php │ │ ├── sl.inc.php │ │ ├── sr.inc.php │ │ ├── sv.inc.php │ │ ├── ta.inc.php │ │ ├── th.inc.php │ │ ├── tr.inc.php │ │ ├── uk.inc.php │ │ ├── uz.inc.php │ │ ├── vi.inc.php │ │ ├── xx.inc.php │ │ ├── zh-tw.inc.php │ │ └── zh.inc.php │ ├── privileges.inc.php │ ├── procedure.inc.php │ ├── processlist.inc.php │ ├── schema.inc.php │ ├── scheme.inc.php │ ├── script.inc.php │ ├── select.inc.php │ ├── sequence.inc.php │ ├── sql.inc.php │ ├── sqlite.php │ ├── static/ │ │ ├── dark.css │ │ ├── default.css │ │ ├── editing.js │ │ └── functions.js │ ├── table.inc.php │ ├── trigger.inc.php │ ├── type.inc.php │ ├── user.inc.php │ ├── variables.inc.php │ └── view.inc.php ├── compile.php ├── composer.json ├── coverage.php ├── designs/ │ ├── README.md │ ├── adminer-dark/ │ │ ├── README.md │ │ └── adminer-dark.css │ ├── brade/ │ │ ├── README.md │ │ └── adminer.css │ ├── bueltge/ │ │ ├── README.md │ │ └── adminer.css │ ├── dracula/ │ │ ├── README.md │ │ └── adminer-dark.css │ ├── esterka/ │ │ ├── README.md │ │ └── adminer.css │ ├── flat/ │ │ ├── README.md │ │ └── adminer.css │ ├── galkaev/ │ │ ├── README.md │ │ └── adminer-dark.css │ ├── haeckel/ │ │ ├── README.md │ │ └── adminer.css │ ├── hever/ │ │ ├── README.md │ │ └── adminer.css │ ├── konya/ │ │ ├── README.md │ │ └── adminer.css │ ├── lavender-light/ │ │ ├── README.md │ │ └── adminer.css │ ├── lucas-sandery/ │ │ ├── README.md │ │ └── adminer.css │ ├── mancave/ │ │ ├── README.md │ │ └── adminer-dark.css │ ├── mvt/ │ │ ├── README.md │ │ └── adminer.css │ ├── nette/ │ │ ├── README.md │ │ └── adminer.css │ ├── ng9/ │ │ ├── README.md │ │ └── adminer.css │ ├── nicu/ │ │ ├── README.md │ │ └── adminer.css │ ├── pappu687/ │ │ ├── README.md │ │ └── adminer.css │ ├── paranoiq/ │ │ ├── README.md │ │ └── adminer.css │ ├── pepa-linha/ │ │ ├── README.md │ │ └── adminer.css │ ├── pokorny/ │ │ ├── README.md │ │ └── adminer.css │ ├── price/ │ │ ├── README.md │ │ └── adminer.css │ ├── rmsoft/ │ │ ├── README.md │ │ └── adminer.css │ ├── rmsoft_blue/ │ │ ├── README.md │ │ └── adminer.css │ ├── rmsoft_blue-dark/ │ │ ├── README.md │ │ └── adminer.css │ └── win98/ │ ├── README.md │ └── adminer.css ├── developing.md ├── editor/ │ ├── db.inc.php │ ├── example.php │ ├── include/ │ │ ├── adminer.inc.php │ │ ├── connect.inc.php │ │ └── editing.inc.php │ ├── index.php │ ├── script.inc.php │ ├── sqlite.php │ └── static/ │ └── editing.js ├── eslint.config.mjs ├── lang.php ├── phpcs.xml ├── phpstan.neon ├── plugins/ │ ├── README.md │ ├── adminer.js.php │ ├── backward-keys.php │ ├── before-unload.php │ ├── config.php │ ├── dark-switcher.php │ ├── database-hide.php │ ├── designs.php │ ├── drivers/ │ │ ├── README.md │ │ ├── clickhouse.php │ │ ├── elastic.php │ │ ├── firebird.php │ │ ├── igdb.php │ │ ├── imap.php │ │ ├── mongo.php │ │ └── simpledb.php │ ├── dump-alter.php │ ├── dump-bz2.php │ ├── dump-date.php │ ├── dump-json.php │ ├── dump-php.php │ ├── dump-xml.php │ ├── dump-zip.php │ ├── edit-calendar.php │ ├── edit-foreign.php │ ├── edit-textarea.php │ ├── editor-setup.php │ ├── editor-views.php │ ├── email-table.php │ ├── enum-option.php │ ├── file-upload.php │ ├── foreign-system.php │ ├── frames.php │ ├── highlight-codemirror.php │ ├── highlight-monaco.php │ ├── highlight-prism.php │ ├── json-column.php │ ├── login-ip.php │ ├── login-otp.php │ ├── login-password-less.php │ ├── login-servers.php │ ├── login-ssl.php │ ├── login-table.php │ ├── master-slave.php │ ├── menu-links.php │ ├── pretty-json-column.php │ ├── row-numbers.php │ ├── select-email.php │ ├── slugify.php │ ├── sql-gemini.php │ ├── sql-log.php │ ├── table-indexes-structure.php │ ├── table-structure.php │ ├── tables-filter.php │ ├── timeout.php │ ├── tinymce.php │ ├── translation.php │ ├── version-github.php │ └── version-noverify.php ├── tests/ │ ├── add-test.php │ ├── cockroachdb.html │ ├── elastic.html │ ├── generate-pdo.php │ ├── mariadb.html │ ├── mssql.html │ ├── mysql.html │ ├── pgsql.html │ ├── screenshots.html │ ├── screenshots.php │ └── sqlite.html └── todo.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # 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 ================================================ FILE: .gitattributes ================================================ /.gitattributes export-ignore /.github export-ignore /.gitignore export-ignore /.gitmodules export-ignore /.travis.yml export-ignore /tests export-ignore ================================================ FILE: .github/FUNDING.yml ================================================ github: vrana patreon: jakubvrana custom: ["https://www.paypal.com/donate/?hosted_button_id=6PK5VNUCFT3FG"] ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- 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:** ================================================ FILE: .github/workflows/ci.yml ================================================ 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 ================================================ FILE: .gitignore ================================================ /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 ================================================ FILE: .gitmodules ================================================ [submodule "jush"] path = externals/jush url = https://github.com/vrana/jush [submodule "JsShrink"] path = externals/JsShrink url = https://github.com/vrana/JsShrink [submodule "PhpShrink"] path = externals/PhpShrink url = https://github.com/vrana/PhpShrink ================================================ FILE: CHANGELOG.md ================================================ ## Adminer dev - Tables overview: allow sorting (bug #1231) - Select: Disable Ctrl+click inline edit without UPDATE privilege - Select: Display NULL in column title - Export: Remember unchecked objects (regression from 5.0.6) - Foreign key: Display new field in case of an error - PostgreSQL: Order NULL last - PostgreSQL: Display all SQL command warnings and only once - PostgreSQL: Export serial as serial, not nextval() - PostgreSQL: Export schema in nextval() - PostgreSQL: Export schema in REFERENCES - Editor: Display tinyint(1) as checkbox (bug #1246, regression from 5.4.2) - Croatian translation ## 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/ ================================================ FILE: CONTRIBUTING.md ================================================ - 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). ================================================ FILE: LICENSE ================================================ Apache License 2.0 or GPL 2 ================================================ FILE: Makefile ================================================ ROOT_DIRECTORY = $(shell dirname "$(realpath $(lastword $(MAKEFILE_LIST)))") PHP := $(shell which php) PORT := 8000 .DEFAULT_GOAL := default .PHONY: default default: compile .PHONY: compile compile: $(PHP) $(ROOT_DIRECTORY)/compile.php .PHONY: server server: php \ --server 127.0.0.1:$(PORT) \ --docroot $(ROOT_DIRECTORY) .PHONY: initialize initialize: git \ -C $(ROOT_DIRECTORY) \ submodule \ update \ --init \ --recursive .PHONY: clean clean: rm \ --recursive \ --force \ $(ROOT_DIRECTORY)/adminer.php .PHONY: clean.all clean.all: clean ================================================ FILE: README.md ================================================ # 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/). ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions Only the latest published version and the latest development version (last commit) are supported. ## Reporting a Vulnerability To report a vulnerability, create a new draft security advisory at [GitHub Security Advisories](https://github.com/vrana/adminer/security/advisories/new). 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. ================================================ FILE: adminer/call.inc.php ================================================ $field) { if (substr($field["inout"], -3) == "OUT" && JUSH == 'sql') { $out[$i] = "@" . idf_escape($field["field"]) . " AS " . idf_escape($field["field"]); } if (!$field["inout"] || substr($field["inout"], 0, 2) == "IN") { $in[] = $i; } } if (!$error && $_POST) { $call = array(); foreach ($routine["fields"] as $key => $field) { $val = ""; if (in_array($key, $in)) { $val = process_input($field); if ($val === false) { $val = "''"; } if (isset($out[$key])) { connection()->query("SET @" . idf_escape($field["field"]) . " = $val"); } } if (isset($out[$key])) { $call[] = "@" . idf_escape($field["field"]); } elseif (in_array($key, $in)) { $call[] = $val; } } $query = (isset($_GET["callf"]) ? "SELECT " : "CALL ") . (idx($routine["returns"], "type") == "record" ? "* FROM " : "") . table($PROCEDURE) . "(" . implode(", ", $call) . ")"; $start = microtime(true); $result = connection()->multi_query($query); $affected = connection()->affected_rows; // getting warnings overwrites this echo adminer()->selectQuery($query, $start, !$result); if (!$result) { echo "

" . error() . "\n"; } else { $connection2 = connect(); if ($connection2) { $connection2->select_db(DB); } do { $result = connection()->store_result(); if (is_object($result)) { 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()); if ($out) { print_select_result(connection()->query("SELECT " . implode(", ", $out))); } } } ?>

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

', 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'])) ) ); ?>
================================================ FILE: adminer/check.inc.php ================================================ $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", ), "?"); ?>

================================================ FILE: adminer/create.inc.php ================================================ partitionBy; $partitions_info = ($partition_by ? driver()->partitionsInfo($TABLE) : array()); $referencable_primary = referencable_primary($TABLE); $foreign_keys = array(); foreach ($referencable_primary as $table_name => $field) { $foreign_keys[str_replace("`", "``", $table_name) . "`" . str_replace("`", "``", $field["field"])] = $table_name; // not idf_escape() - used in JS } $orig_fields = array(); $table_status = array(); if ($TABLE != "") { $orig_fields = fields($TABLE); $table_status = table_status1($TABLE); if (count($table_status) < 2) { // there's only the Name field $error = lang('No tables.'); } } $row = $_POST; $row["fields"] = (array) $row["fields"]; if ($row["auto_increment_col"]) { $row["fields"][$row["auto_increment_col"]]["auto_increment"] = true; } if ($_POST) { save_settings(array("comments" => $_POST["comments"], "defaults" => $_POST["defaults"])); } if ($_POST && !process_fields($row["fields"]) && !$error) { if ($_POST["drop"]) { queries_redirect(substr(ME, 0, -1), lang('Table has been dropped.'), drop_tables(array($TABLE))); } else { $fields = array(); $all_fields = array(); $use_all_fields = false; $foreign = array(); $orig_field = reset($orig_fields); $after = " FIRST"; foreach ($row["fields"] as $key => $field) { $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["generated"]) { $field["default"] = null; } $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)) { $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( 'table' => $foreign_keys[$field["type"]], 'source' => array($field["field"]), 'target' => array($type_field["field"]), 'on_delete' => $field["on_delete"], )); } $after = " AFTER " . idf_escape($field["field"]); } elseif ($field["orig"] != "") { $use_all_fields = true; $fields[] = array($field["orig"]); } if ($field["orig"] != "") { $orig_field = next($orig_fields); if (!$orig_field) { $after = ""; } } } $partitioning = array(); if (in_array($row["partition_by"], $partition_by)) { foreach ($row as $key => $val) { if (preg_match('~^partition~', $key)) { $partitioning[$key] = $val; } } 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.'); if ($TABLE == "") { cookie("adminer_engine", $row["Engine"]); $message = lang('Table has been created.'); } $name = trim($row["name"]); queries_redirect(ME . (support("table") ? "table=" : "select=") . urlencode($name), $message, alter_table( $TABLE, $name, (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"] : ""), ($row["Collation"] && $row["Collation"] != $table_status["Collation"] ? $row["Collation"] : ""), ($row["Auto_increment"] != "" ? number($row["Auto_increment"]) : ""), $partitioning )); } } 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" => "")), "partition_names" => array(""), ); if ($TABLE != "") { $row = $table_status; $row["name"] = $TABLE; $row["fields"] = array(); if (!$_GET["auto_increment"]) { // don't prefill by original Auto_increment for the sake of performance and not reusing deleted ids $row["Auto_increment"] = ""; } foreach ($orig_fields as $field) { $field["generated"] = $field["generated"] ?: (isset($field["default"]) ? "DEFAULT" : ""); $row["fields"][] = $field; } if ($partition_by) { $row += $partitions_info; $row["partition_names"][] = ""; $row["partition_values"][] = ""; } } } $collations = collations(); 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"])) { $row["Engine"] = $engine; break; } } ?>

\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"]) ? "" : '' ) : '') ; ?>

" . 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(); ?>

================================================ FILE: adminer/database.inc.php ================================================

' . h($name) . '
' : '' ) . "\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' => "relational-databases/system-functions/sys-fn-helpcollations-transact-sql", )) : ""); ?> " . confirm(lang('Drop %s?', DB)) . "\n"; } elseif (!$_POST["add"] && $_GET["db"] == "") { echo icon("plus", "add[0]", "+", lang('Add next')) . "\n"; } echo input_token(); ?>

================================================ FILE: adminer/db.inc.php ================================================ 1 && ($_POST["drop"] || $_POST["truncate"] || $_POST["copy"])) { queries("SET foreign_key_checks = 0"); // allows to truncate or drop several tables at once } if ($_POST["truncate"]) { if ($_POST["tables"]) { $result = truncate_tables($_POST["tables"]); } $message = lang('Tables have been truncated.'); } elseif ($_POST["move"]) { $result = move_tables((array) $_POST["tables"], (array) $_POST["views"], $_POST["target"]); $message = lang('Tables have been moved.'); } elseif ($_POST["copy"]) { $result = copy_tables((array) $_POST["tables"], (array) $_POST["views"], $_POST["target"]); $message = lang('Tables have been copied.'); } elseif ($_POST["drop"]) { if ($_POST["views"]) { $result = drop_views($_POST["views"]); } if ($result && $_POST["tables"]) { $result = drop_tables($_POST["tables"]); } $message = lang('Tables have been dropped.'); } 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('Adminer\idf_escape', $_POST["tables"])))) { while ($row = $result->fetch_assoc()) { $message .= "" . h($row["Table"]) . ": " . h($row["Msg_text"]) . "
"; } } queries_redirect($_SERVER["REQUEST_URI"], $message, $result); } page_header(($_GET["ns"] == "" ? lang('Database') . ": " . h(DB) : lang('Schema') . ": " . h($_GET["ns"])), $error, true); if (adminer()->homepage()) { if ($_GET["ns"] !== "") { $order = $_GET["order"]; echo "

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

\n"; $tables_list = ($order ? table_status() : tables_list()); if (!$tables_list) { echo "

" . lang('No tables.') . "\n"; } else { echo "

\n"; if (support("table")) { echo "
" . lang('Search data in tables') . "
"; 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"] = $_POST["op"]; search_tables(); } } echo "
\n"; echo "\n"; echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});"); echo ''; echo '\n"; if ($order) { uasort($tables_list, function ($a, $b) use ($order) { $return = ($a[$order] < $b[$order] ? -1 : ($a[$order] > $b[$order] ? 1 : 0)); // <=> available since PHP 7.1 return (in_array($order, array('Engine', 'Collation', 'Comment')) ? $return : -$return); }); } $tables = 0; foreach ($tables_list as $name => $status) { $view = ($order ? is_view($status) : $status !== null && !preg_match('~table|sequence~i', $status)); $status = ($order ? $status : array('Engine' => $status)); $id = h("Table-" . $name); echo '
' . script("qs('#check-all').onclick = partial(formCheck, /^(tables|views)\[/);", ""); echo '' . lang('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 $key => $column) { echo "$column[0]"; } 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 && !preg_match('~materialized~i', $status['Engine'])) { $title = lang('View'); echo '' . (support("view") ? "$title" : $title); echo '?'; echo '' . h($status['Comment']); } else { foreach ($columns as $key => $column) { $id = " id='$key-" . h($name) . "'"; $val = idx($status, $key, '?'); echo ($column[1] ? "" . (is_numeric($val) ? ($val < 0 ? '?' : ($key == "Rows" && $val && $status["Engine"] == (JUSH == "pgsql" ? "table" : "InnoDB") ? '~ ' : '') . format_number($val)) : $val ) . "" : "" . h($val) ); } $tables++; } echo "\n"; } echo "
" . lang('%d in total', count($tables_list)); echo "" . h(JUSH == "sql" ? get_val("SELECT @@default_storage_engine") : ""); echo (collations() ? "" . h(db_collation(DB, collations())) : ''); foreach (array("Data_length", "Index_length", "Data_free") as $key) { echo ($columns[$key] ? "" : ""); } echo "\n"; echo "
\n"; echo ($order ? '' : script("ajaxSetHtml('" . js_escape(ME) . "script=db');")); echo "
\n"; if (!information_schema(DB)) { $vacuum = " " . on_help("'VACUUM'"); $optimize = " " . on_help(JUSH == "sql" ? "'OPTIMIZE TABLE'" : "'VACUUM ANALYZE'"); $print = (JUSH == "sqlite" ? $vacuum . " " . on_help("'PRAGMA integrity_check'") : (JUSH == "pgsql" ? $vacuum . $optimize : (JUSH == "sql" ? " " . on_help("'ANALYZE TABLE'") . $optimize . " " . 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 ($print ? "\n"; } echo "
\n"; echo script("tableCheck();"); } echo (function_exists('Adminer\alter_table') ? "

" . lang('Routines') . "

\n"; $routines = routines(); if ($routines) { echo "\n"; echo '\n"; 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 '
' . lang('Name') . '' . lang('Type') . '' . lang('Return type') . "
' . h($row["ROUTINE_NAME"]) . ''; echo '' . h($row["ROUTINE_TYPE"]); echo '' . h($row["DTD_IDENTIFIER"]); echo '' . lang('Alter') . ""; } echo "
\n"; } 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"; foreach ($sequences as $val) { echo "
" . lang('Name') . "
" . h($val) . "\n"; } echo "
\n"; } echo "

" . lang('User types') . "

\n"; $user_types = types(); if ($user_types) { echo "\n"; echo "\n"; foreach ($user_types as $val) { echo "
" . lang('Name') . "
" . h($val) . "\n"; } echo "
\n"; } echo "

" . lang('Events') . "

\n"; $rows = get_rows("SHOW EVENTS"); if ($rows) { echo "\n"; echo "\n"; foreach ($rows as $row) { echo ""; echo "
" . lang('Name') . "" . lang('Schedule') . "" . lang('Start') . "" . lang('End') . "
" . h($row["Name"]); echo "" . ($row["Execute at"] ? lang('At given time') . "" . $row["Execute at"] : lang('Every') . " " . $row["Interval value"] . " " . $row["Interval field"] . "$row[Starts]"); echo "$row[Ends]"; echo '' . lang('Alter') . ''; } echo "
\n"; $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"); } echo "
" . lang('Output') . "" . html_radios("output", adminer()->dumpOutput(), $row["output"]) . "\n"; 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"]) . (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')) : "") ); echo "
" . lang('Tables') . "" . html_select('table_style', $table_style, $row["table_style"]) . checkbox("auto_increment", 1, $row["auto_increment"], lang('Auto Increment')) . (support("trigger") ? checkbox("triggers", 1, $row["triggers"], lang('Triggers')) : "") ; echo "
" . lang('Data') . "" . html_select('data_style', $data_style, $row["data_style"]); ?>

"; echo "\n"; $databases = adminer()->databases(); if ($databases) { foreach ($databases as $db) { if (!information_schema($db)) { $prefix = preg_replace('~_.*~', '', $db); echo "
" . script("qs('#check-tables').onclick = partial(formCheck, /^tables\\[/);", ""); echo "" . script("qs('#check-data').onclick = partial(formCheck, /^data\\[/);", ""); echo "\n"; $views = ""; $tables_list = tables_list(); foreach ($tables_list as $name => $type) { $prefix = preg_replace('~_.*~', '', $name); $checked = ($TABLE == "" || $TABLE == (substr($TABLE, -1) == "%" ? "$prefix%" : $name)); //! % may be part of table name $print = "
" . checkbox("tables[]", $name, $checked, $name, "", "block"); if ($type !== null && !preg_match('~table~i', $type)) { $views .= "$print\n"; } else { echo "$print\n"; } $prefixes[$prefix]++; } echo $views; if ($tables_list) { echo script("ajaxSetHtml('" . js_escape(ME) . "script=db');"); } } else { echo "
"; echo ""; echo script("qs('#check-databases').onclick = partial(formCheck, /^databases\\[/);", ""); echo "
" . checkbox("databases[]", $db, $TABLE == "" || $TABLE == "$prefix%", $db, "", "block") . "\n"; $prefixes[$prefix]++; } } } else { echo "
"; } } ?>

$val) { if ($key != "" && $val > 1) { echo ($first ? "

" : " ") . "" . h($key) . ""; $first = false; } } ================================================ FILE: adminer/edit.inc.php ================================================ $field) { if ((!$update && !isset($field["privileges"]["insert"])) || adminer()->fieldName($field) == "") { unset($fields[$name]); } } if ($_POST && !$error && !isset($_GET["select"])) { $location = $_POST["referer"]; if ($_POST["insert"]) { // continue edit or insert $location = ($update ? null : $_SERVER["REQUEST_URI"]); } elseif (!preg_match('~^.+&select=.+$~', $location)) { $location = ME . "select=" . urlencode($TABLE); } $indexes = indexes($TABLE); $unique_array = unique_array($_GET["where"], $indexes); $query_where = "\nWHERE $where"; if (isset($_POST["delete"])) { queries_redirect( $location, lang('Item has been deleted.'), driver()->delete($TABLE, $query_where, $unique_array ? 0 : 1) ); } else { $set = array(); foreach ($fields as $name => $field) { $val = process_input($field); if ($val !== false && $val !== null) { $set[idf_escape($name)] = $val; } } if ($update) { if (!$set) { redirect($location); } queries_redirect( $location, lang('Item has been updated.'), driver()->update($TABLE, $set, $query_where, $unique_array ? 0 : 1) ); if (is_ajax()) { page_headers(); page_messages($error); exit; } } else { $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 ($where) { $select = array(); foreach ($fields as $name => $field) { if (isset($field["privileges"]["select"])) { $as = ($_POST["clone"] && $field["auto_increment"] ? "''" : convert_field($field)); $select[] = ($as ? "$as AS " : "") . idf_escape($name); } } $row = array(); if (!support("table")) { $select = array("*"); } if ($select) { $result = driver()->select($TABLE, $select, array($where), $select, array(), (isset($_GET["select"]) ? 2 : 1)); if (!$result) { $error = error(); } else { $row = $result->fetch_assoc(); if (!$row) { // MySQLi returns null $row = false; } } if (isset($_GET["select"]) && (!$row || $result->fetch_assoc())) { // $result->num_rows != 1 isn't available in all drivers $row = null; } } } if (!support("table") && !$fields) { // used by Mongo and SimpleDB if (!$where) { // insert $result = driver()->select($TABLE, array("*"), array(), array("*")); $row = ($result ? $result->fetch_assoc() : false); if (!$row) { $row = array(driver()->primary => ""); } } if ($row) { foreach ($row as $key => $val) { if (!$where) { $row[$key] = null; } $fields[$key] = array("field" => $key, "null" => ($key != driver()->primary), "auto_increment" => ($key == driver()->primary)); } } } if ($_POST["save"]) { $row = (array) $_POST["fields"] + ($row ? $row : array()); } edit_form($TABLE, $fields, $row, $update, $error); ================================================ FILE: adminer/elastic.php ================================================ "ENABLE", "DISABLED" => "DISABLE", "SLAVESIDE_DISABLED" => "DISABLE ON SLAVE"); $row = $_POST; if ($_POST && !$error) { if ($_POST["drop"]) { query_redirect("DROP EVENT " . idf_escape($EVENT), substr(ME, 0, -1), lang('Event has been dropped.')); } elseif (in_array($row["INTERVAL_FIELD"], $intervals) && isset($statuses[$row["STATUS"]])) { $schedule = "\nON SCHEDULE " . ($row["INTERVAL_VALUE"] ? "EVERY " . q($row["INTERVAL_VALUE"]) . " $row[INTERVAL_FIELD]" . ($row["STARTS"] ? " STARTS " . q($row["STARTS"]) : "") . ($row["ENDS"] ? " ENDS " . q($row["ENDS"]) : "") //! ALTER EVENT doesn't drop ENDS - MySQL bug #39173 : "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]", ";") . ";" ) ); } } page_header(($EVENT != "" ? lang('Alter event') . ": " . h($EVENT) : lang('Create event')), $error); if (!$row && $EVENT != "") { $rows = get_rows("SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = " . q(DB) . " AND EVENT_NAME = " . q($EVENT)); $row = reset($rows); } ?>

" data-maxlength="64" autocapitalize="off">
">
">
" class="size">
" data-maxlength="64">

================================================ FILE: adminer/file.inc.php ================================================ $val) { $target[$key] = $row["target"][$key]; } $row["target"] = $target; } if (JUSH == "sqlite") { $result = recreate_table($TABLE, $TABLE, array(), array(), array(" $name" => ($row["drop"] ? "" : " " . format_foreign_key($row)))); } else { $alter = "ALTER TABLE " . table($TABLE); $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)); if ($_POST) { ksort($row["source"]); if ($_POST["change"] || $_POST["change-js"]) { $row["target"] = array(); } else { $row["source"][] = ""; } } elseif ($name != "") { $foreign_keys = foreign_keys($TABLE); $row = $foreign_keys[$name]; $row["source"][] = ""; } else { $row["table"] = $TABLE; $row["source"] = array(""); } ?>
select_db($row["db"]); } if ($row["ns"] != "") { $orig_schema = get_schema(); set_schema($row["ns"]); } $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 "

\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) { if (!information_schema($db)) { $dbs[] = $db; } } 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);" : ""), "label-source"); echo "" . html_select("target[" . (+$key) . "]", $target, idx($row["target"], $key), "", "label-target"); $j++; } ?>

"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", )); ?>

================================================ FILE: adminer/include/adminer.inc.php ================================================ f() instead of $this->f() to give chance to other plugins */ class Adminer { /** @var Adminer|Plugins */ static $instance; /** @visibility protected(set) */ public string $error = ''; // HTML /** Name in title and navigation * @return string HTML code */ function name(): string { return "Adminer"; } /** Connection parameters * @return array{string, string, string} */ function credentials(): array { return array(SERVER, $_GET["username"], get_password()); } /** Get SSL connection options * @return string[]|void */ function connectSsl() { } /** Get key used for permanent login * @return string cryptic string which gets combined with password or '' in case of an error */ 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 */ function bruteForceKey(): string { return $_SERVER["REMOTE_ADDR"]; } /** Get server name displayed in breadcrumbs * @return string HTML code or null */ function serverName(?string $server): string { return h($server); } /** Identifier of selected database */ function database(): ?string { // should be used everywhere instead of DB return DB; } /** Get cached list of databases * @return list */ 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 list */ function schemas(): array { return schemas(); } /** Specify limit for waiting on some slow queries like DB list * @return float number of seconds */ function queryTimeout(): float { return 2; } /** Called after connecting and selecting a database */ function afterConnect(): void { } /** Headers to send before HTML output */ function headers(): void { } /** Get Content Security Policy headers * @param list $csp of arrays with directive name in key, allowed sources in value * @return list same as $csp */ function csp(array $csp): array { return $csp; } /** Print HTML code inside * @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(?bool $dark = null): bool { // this is matched by compile.php echo "\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 string[] key is URL, value is either 'light' (supports only light color scheme), 'dark' or '' (both) */ function css(): array { $return = array(); 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 */ 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 $heading HTML * @param string $value HTML */ function loginFormField(string $name, string $heading, string $value): string { return $heading . $value . "\n"; } /** Authorize the user * @return mixed true for success, string for error message, false for unknown error */ function login(string $login, string $password) { if ($password == "") { return lang('Adminer does not support accessing a database without a password, more information.', target_blank()); } return true; } /** Table caption used in navigation and headings * @param TableStatus $tableStatus * @return string HTML code, "" to ignore table */ function tableName(array $tableStatus): string { return h($tableStatus["Name"]); } /** Field caption used in select and edit * @param Field|RoutineField $field * @param int $order order of column in select * @return string HTML code, "" to ignore field */ function fieldName(array $field, int $order = 0): string { $type = $field["full_type"] . ($field["null"] ? " NULL" : ""); $comment = $field["comment"]; return '' . h($field["field"]) . ''; } /** Print links after select heading * @param TableStatus $tableStatus * @param ?string $set new item options, NULL for no new item */ function selectLinks(array $tableStatus, ?string $set = ""): void { $name = $tableStatus["Name"]; echo '

\n"; // required for IE9 inline edit 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) . ")" . (support("sql") ? " " . lang('Edit') . "" : "") . $return ; } /** Query printed in SQL command before execution * @param string $query query to be executed * @return string escaped query to be printed */ 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 * @return string SQL expression, empty string for no description */ function rowDescription(string $table): string { return ""; } /** Get descriptions of selected data * @param list $rows all data to print * @param list[] $foreignKeys * @return list */ function rowDescriptions(array $rows, array $foreignKeys): array { return $rows; } /** Get a link to use in select table * @param string $val raw value of the field * @param array{type: string} $field * @return string|void null to create the default link */ function selectLink(?string $val, array $field) { } /** Value printed in select table * @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)) . ""; } return ($link ? "$return" : $return); } /** Value conversion used in select and edit * @param array{type: string} $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 Field[] $fields * @param TableStatus $tableStatus */ function tableStructurePrint(array $fields, ?array $tableStatus = null): void { echo "

\n"; echo "\n"; echo "\n"; $structured_types = driver()->structuredTypes(); foreach ($fields as $field) { echo "
" . lang('Column') . "" . lang('Type') . (support("comment") ? "" . lang('Comment') : "") . "
" . 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') . "" : ""); $default = h($field["default"]); echo (isset($field["default"]) ? " [" . ($field["generated"] ? "$default" : $default) . "]" : ""); echo (support("comment") ? "" . h($field["comment"]) : ""); echo "\n"; } echo "
\n"; echo "
\n"; } /** Print list of indexes on table in tabular format * @param Index[] $indexes * @param TableStatus $tableStatus */ 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(); foreach ($index["columns"] as $key => $val) { $print[] = "" . h($val) . "" . ($index["lengths"][$key] ? "(" . $index["lengths"][$key] . ")" : "") . ($index["descs"][$key] ? " DESC" : "") ; } echo ""; 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 list $select result of selectColumnsProcess()[0] * @param string[] $columns selectable columns */ function selectColumnsPrint(array $select, array $columns): void { print_fieldset("select", lang('Select'), $select); $i = 0; $select[""] = array(); foreach ($select as $key => $val) { $val = idx($_GET["columns"], $key, array()); $column = select_input( " name='columns[$i][col]'", $columns, $val["col"], ($key !== "" ? "selectFieldChange" : "selectAddRow") ); 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++; } echo "\n"; } /** Print search box in select * @param list $where result of selectSearchProcess() * @param string[] $columns selectable columns * @param Index[] $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('Adminer\h', $index["columns"])) . ") AGAINST"; echo " "; echo script("qsl('input').oninput = selectFieldChange;", ""); 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"], adminer()->operators()))) { echo "
" . select_input( " name='where[$i][col]'", $columns, $val["col"], ($val ? "selectFieldChange" : "selectAddRow"), "(" . lang('anywhere') . ")" ); 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"; } } echo "\n"; } /** Print order box in select * @param list $order result of selectOrderProcess() * @param string[] $columns selectable columns * @param Index[] $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) { if ($val != "") { echo "
" . select_input(" name='order[$i]'", $columns, $val, "selectFieldChange"); echo checkbox("desc[$i]", 1, isset($_GET["desc"][$key]), lang('descending')) . "
\n"; $i++; } } echo "
" . select_input(" name='order[$i]'", $columns, "", "selectAddRow"); echo checkbox("desc[$i]", 1, false, lang('descending')) . "
\n"; echo "\n"; } /** Print limit box in select */ function selectLimitPrint(int $limit): void { echo "
" . lang('Limit') . "
"; //
for easy styling echo ""; echo script("qsl('input').oninput = selectFieldChange;", ""); echo "
\n"; } /** Print text length box in select * @param numeric-string $text_length result of selectLengthProcess() */ function selectLengthPrint(string $text_length): void { if ($text_length !== null) { echo "
" . lang('Text length') . "
"; echo ""; echo "
\n"; } } /** Print action box in select * @param Index[] $indexes */ function selectActionPrint(array $indexes): void { echo "
" . lang('Action') . "
"; echo ""; echo " "; echo "\n"; echo "const indexColumns = "; $columns = array(); foreach ($indexes as $index) { $current_key = reset($index["columns"]); if ($index["type"] != "FULLTEXT" && $current_key) { $columns[$current_key] = 1; } } $columns[""] = 1; foreach ($columns as $key => $val) { json_row($key); } echo ";\n"; echo "selectFieldChange.call(qs('#form')['select']);\n"; echo "\n"; echo "
\n"; } /** Print command box in select * @return bool whether to print default commands */ function selectCommandPrint(): bool { return !information_schema(DB); } /** Print import box in select * @return bool whether to print default import */ function selectImportPrint(): bool { return !information_schema(DB); } /** Print extra text in the end of a select form * @param string[] $emailFields fields holding e-mails * @param string[] $columns selectable columns */ function selectEmailPrint(array $emailFields, array $columns): void { } /** Process columns box in select * @param string[] $columns selectable columns * @param Index[] $indexes * @return list> [[select_expressions], [group_expressions]] */ 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"], 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"], driver()->grouping)) { $group[] = $select[$key]; } } } return array($select, $group); } /** Process search box in select * @param Field[] $fields * @param Index[] $indexes * @return list expressions to join by AND */ function selectSearchProcess(array $fields, array $indexes): array { $return = array(); foreach ($indexes as $i => $index) { 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) { $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[] = (count($conds) == 1 ? $conds[0] : ($conds ? "(" . implode(" OR ", $conds) . ")" : "1 = 0" )); } } return $return; } /** Process order box in select * @param Field[] $fields * @param Index[] $indexes * @return list expressions to join by comma */ function selectOrderProcess(array $fields, array $indexes): array { $return = array(); foreach ((array) $_GET["order"] as $key => $val) { if ($val != "") { $return[] = (preg_match('~^((COUNT\(DISTINCT |[A-Z0-9_]+\()(`(?:[^`]|``)+`|"(?:[^"]|"")+")\)|COUNT\(\*\))$~', $val) ? $val : idf_escape($val)) //! MS SQL uses [] . (isset($_GET["desc"][$key]) ? " DESC" . (JUSH == 'pgsql' && idx($fields[$val], "null") ? " NULLS LAST" : "") : "") ; } } return $return; } /** Process limit box in select */ function selectLimitProcess(): int { return (isset($_GET["limit"]) ? intval($_GET["limit"]) : 50); } /** Process length box in select * @return numeric-string number of characters to shorten texts, will be escaped, empty string means unlimited */ function selectLengthProcess(): string { return (isset($_GET["text_length"]) ? "$_GET[text_length]" : "100"); } /** Process extras in select form * @param string[] $where AND conditions * @param list[] $foreignKeys * @return bool true if processed, false to process other parts of form */ function selectEmailProcess(array $where, array $foreignKeys): bool { return false; } /** Build SQL query used in select * @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(array $select, array $where, array $group, array $order, int $limit, ?int $page): string { return ""; } /** Query printed after execution in the message * @param string $query executed query * @param string $time elapsed time */ function messageQuery(string $query, string $time, bool $failed = false): string { restart_session(); $history = &get_session("queries"); if (!idx($history, $_GET["db"])) { $history[$_GET["db"]] = array(); } if (strlen($query) > 1e6) { $query = preg_replace('~[\x80-\xFF]+$~', '', substr($query, 0, 1e6)) . "\n…"; // [\x80-\xFF] - valid UTF-8, \n - can end by one-line comment } $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())) { $id = "warnings-" . count($history[$_GET["db"]]); $return = "" . lang('Warnings') . ", $return\n"; } return " " . @date("H:i:s") . "" // @ - time zone may be not set . " $return' ; } /** Print before edit form * @param Field[] $fields * @param mixed $row */ function editRowPrint(string $table, array $fields, $row, ?bool $update): void { } /** Functions displayed in edit form * @param Field|array{null:bool} $field * @return string[] */ function editFunctions(array $field): array { $return = ($field["null"] ? "NULL/" : ""); $update = isset($_GET["select"]) || where($_GET); foreach (array(driver()->insertFunctions, driver()->editFunctions) as $key => $functions) { if (!$key || (!isset($_GET["call"]) && $update)) { // relative functions foreach ($functions as $pattern => $val) { if (!$pattern || preg_match("~$pattern~", $field["type"])) { $return .= "/$val"; } } } if ($key && $functions && !preg_match('~set|bool~', $field["type"]) && !is_blob($field)) { $return .= "/SQL"; } } if ($field["auto_increment"] && !$update) { $return = lang('Auto Increment'); } return explode("/", $return); } /** Get options to display edit field * @param ?string $table null in call.inc.php * @param Field $field * @param string $attrs attributes to use inside the tag * @param string|string[]|false|null $value false means original value * @return string custom input field or empty string for default */ function editInput(?string $table, array $field, string $attrs, $value): string { if ($field["type"] == "enum") { return (isset($_GET["select"]) ? " " : "") . enum_input("radio", $attrs, $field, $value, "NULL") ; } return ""; } /** Get hint for edit field * @param ?string $table null in call.inc.php * @param Field $field */ function editHint(?string $table, array $field, ?string $value): string { return ""; } /** Process sent input * @param Field $field * @return string expression to use in a query */ function processInput(array $field, string $value, ?string $function = ""): string { if ($function == "SQL") { return $value; // SQL injection } $name = $field["field"]; $return = q($value); if (preg_match('~^(now|getdate|uuid)$~', $function)) { $return = "$function()"; } elseif (preg_match('~^current_(date|timestamp)$~', $function)) { $return = $function; } elseif (preg_match('~^([+-]|\|\|)$~', $function)) { $return = idf_escape($name) . " $function $return"; } elseif (preg_match('~^[+-] interval$~', $function)) { $return = idf_escape($name) . " $function " . (preg_match("~^(\\d+|'[0-9.: -]') [A-Z_]+\$~i", $value) && JUSH != "pgsql" ? $value : $return); } elseif (preg_match('~^(addtime|subtime|concat)$~', $function)) { $return = "$function(" . idf_escape($name) . ", $return)"; } elseif (preg_match('~^(md5|sha1|password|encrypt)$~', $function)) { $return = "$function($return)"; } return unconvert_field($field, $return); } /** Return export output options * @return string[] */ function dumpOutput(): array { $return = array('text' => lang('open'), 'file' => lang('save')); if (function_exists('gzencode')) { $return['gz'] = 'gzip'; } return $return; } /** Return export format options * @return string[] empty to disable export */ function dumpFormat(): array { return (support("dump") ? array('sql' => 'SQL') : array()) + array('csv' => 'CSV,', 'csv;' => 'CSV;', 'tsv' => 'TSV'); } /** Export database structure * @return void prints data */ function dumpDatabase(string $db): void { } /** Export table structure * @param int $is_view 0 table, 1 view, 2 temporary view table * @return void prints data */ function dumpTable(string $table, string $style, int $is_view = 0): void { if ($_POST["format"] != "sql") { echo "\xef\xbb\xbf"; // UTF-8 byte order mark if ($style) { dump_csv(array_keys(fields($table))); } } else { if ($is_view == 2) { $fields = array(); foreach (fields($table) as $name => $field) { $fields[] = idf_escape($name) . " $field[full_type]"; } $create = "CREATE TABLE " . table($table) . " (" . implode(", ", $fields) . ")"; } else { $create = create_sql($table, $_POST["auto_increment"], $style); } set_utf8mb4($create); if ($style && $create) { if ($style == "DROP+CREATE" || $is_view == 1) { echo "DROP " . ($is_view == 2 ? "VIEW" : "TABLE") . " IF EXISTS " . table($table) . ";\n"; } if ($is_view == 1) { $create = remove_definer($create); } echo "$create;\n\n"; } } } /** Export table data * @return void prints data */ function dumpData(string $table, string $style, string $query): void { if ($style) { $max_packet = (JUSH == "sqlite" ? 0 : 1048576); // default, minimum is 1024 $fields = array(); $identity_insert = false; if ($_POST["format"] == "sql") { if ($style == "TRUNCATE+INSERT") { echo truncate_sql($table) . ";\n"; } $fields = fields($table); if (JUSH == "mssql") { foreach ($fields as $field) { if ($field["auto_increment"]) { echo "SET IDENTITY_INSERT " . table($table) . " ON;\n"; $identity_insert = true; break; } } } } $result = connection()->query($query, 1); // 1 - MYSQLI_USE_RESULT if ($result) { $insert = ""; $buffer = ""; $keys = array(); $generated = array(); $suffix = ""; $fetch_function = ($table != '' ? 'fetch_assoc' : 'fetch_row'); $count = 0; while ($row = $result->$fetch_function()) { if (!$keys) { $values = array(); foreach ($row as $val) { $field = $result->fetch_field(); if (idx($fields[$field->name], 'generated')) { $generated[$field->name] = true; continue; } $keys[] = $field->name; $key = idf_escape($field->name); $values[] = "$key = VALUES($key)"; } $suffix = ($style == "INSERT+UPDATE" ? "\nON DUPLICATE KEY UPDATE " . implode(", ", $values) : "") . ";\n"; } if ($_POST["format"] != "sql") { if ($style == "table") { dump_csv($keys); $style = "INSERT"; } dump_csv($row); } else { if (!$insert) { $insert = "INSERT INTO " . table($table) . " (" . implode(", ", array_map('Adminer\idf_escape', $keys)) . ") VALUES"; } foreach ($row as $key => $val) { if ($generated[$key]) { unset($row[$key]); continue; } $field = $fields[$key]; $row[$key] = ($val !== null ? unconvert_field($field, preg_match(number_type(), $field["type"]) && !preg_match('~\[~', $field["full_type"]) && is_numeric($val) ? $val : q(($val === false ? 0 : $val))) : "NULL" ); } $s = ($max_packet ? "\n" : " ") . "(" . implode(",\t", $row) . ")"; if (!$buffer) { $buffer = $insert . $s; } elseif (JUSH == 'mssql' ? $count % 1000 != 0 // https://learn.microsoft.com/en-us/sql/t-sql/queries/table-value-constructor-transact-sql#limitations-and-restrictions : strlen($buffer) + 4 + strlen($s) + strlen($suffix) < $max_packet // 4 - length specification ) { $buffer .= ",$s"; } else { echo $buffer . $suffix; $buffer = $insert . $s; } } $count++; } if ($buffer) { echo $buffer . $suffix; } } elseif ($_POST["format"] == "sql") { echo "-- " . str_replace("\n", " ", connection()->error) . "\n"; } if ($identity_insert) { echo "SET IDENTITY_INSERT " . table($table) . " OFF;\n"; } } } /** Set export filename * @return string filename without extension */ function dumpFilename(string $identifier): string { return friendly_url($identifier != "" ? $identifier : (SERVER ?: "localhost")); } /** Send headers for export * @return string extension */ function dumpHeaders(string $identifier, bool $multi_table = false): string { $output = $_POST["output"]; $ext = (preg_match('~sql~', $_POST["format"]) ? "sql" : ($multi_table ? "tar" : "csv")); // multiple CSV packed to TAR header("Content-Type: " . ($output == "gz" ? "application/x-gzip" : ($ext == "tar" ? "application/x-tar" : ($ext == "sql" || $output != "file" ? "text/plain" : "text/csv") . "; charset=utf-8" ))); if ($output == "gz") { ob_start(function ($string) { // ob_start() callback receives an optional parameter $phase but gzencode() accepts optional parameter $level return gzencode($string); }, 1e6); } return $ext; } /** Print text after export * @return void prints data */ function dumpFooter(): void { if ($_POST["format"] == "sql") { echo "-- " . gmdate("Y-m-d H:i:s e") . "\n"; } } /** Set the path of the file for webserver load * @return string path of the sql dump file */ function importServerPath(): string { return "adminer.sql"; } /** Print homepage * @return bool whether to print default homepage */ function homepage(): bool { echo '

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

\n"; // this is matched by compile.php switch_lang(); if ($missing == "auth") { $output = ""; foreach ((array) $_SESSION["pwds"] as $vendor => $servers) { foreach ($servers as $server => $usernames) { $name = h(get_setting("vendor-$vendor-$server") ?: get_driver($vendor)); foreach ($usernames as $username => $password) { if ($password !== null) { $dbs = $_SESSION["db"][$vendor][$server][$username]; foreach (($dbs ? array_keys($dbs) : array("")) as $db) { $output .= "
  • ($name) " . h("$username@" . ($server != "" ? adminer()->serverName($server) : "") . ($db != "" ? " - $db" : "")) . "\n"; } } } } } if ($output) { echo "
      \n$output
    \n" . script("mixin(qs('#logins'), {onmouseover: menuOver, onmouseout: menuOut});"); } } else { $tables = array(); if ($_GET["ns"] !== "" && !$missing && DB != "") { connection()->select_db(DB); $tables = table_status('', true); } adminer()->syntaxHighlighting($tables); adminer()->databasesPrint($missing); $actions = array(); if (DB == "" || !$missing) { if (support("sql")) { $actions[] = "" . lang('SQL command') . ""; $actions[] = "" . lang('Import') . ""; } $actions[] = "" . lang('Export') . ""; } $in_db = $_GET["ns"] !== "" && !$missing && DB != ""; if ($in_db && function_exists('Adminer\alter_table')) { $actions[] = '" . lang('Create table') . ""; } echo ($actions ? "

    " . lang('No tables.') . "

    \n"; } } } } /** Set up syntax highlight for code and "; } /** Generate HTML if $options are empty * @param string[] $options */ function select_input(string $attrs, array $options, ?string $value = "", string $onchange = "", string $placeholder = ""): string { $tag = ($options ? "select" : "input"); return "<$tag$attrs" . ($options ? ">