From 3de838dded3fdd41c8aa75e8ac05cb0dcc510964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Mon, 5 Aug 2024 00:37:06 +0200 Subject: [PATCH] feat: start implementation to get Wikipedia dump and customize it --- .gitignore | 2 + TODO.md | 18 +- data/.env.example | 3 + data/.eslintrc.json | 4 + data/README.md | 124 ++- data/adminer/default-orange.css | 1376 +++++++++++++++++++++++++ data/adminer/fonts/entypo.eot | Bin 0 -> 21233 bytes data/adminer/fonts/entypo.svg | 264 +++++ data/adminer/fonts/entypo.ttf | Bin 0 -> 36860 bytes data/adminer/fonts/entypo.woff | Bin 0 -> 23972 bytes data/adminer/logo.png | Bin 0 -> 1332 bytes data/compose.yaml | 71 ++ data/database-wikipedia-v2.js | 113 ++ data/database-wikipedia.js | 94 ++ data/database-wikipedia.sh | 79 ++ data/sql/0-insert-optimizer-start.sql | 4 + data/sql/1-pages-create.sql | 9 + data/sql/99-insert-optimizer-end.sql | 4 + data/test.js | 48 + data/utils.js | 63 ++ 20 files changed, 2267 insertions(+), 9 deletions(-) create mode 100644 data/.env.example create mode 100644 data/.eslintrc.json create mode 100644 data/adminer/default-orange.css create mode 100644 data/adminer/fonts/entypo.eot create mode 100644 data/adminer/fonts/entypo.svg create mode 100644 data/adminer/fonts/entypo.ttf create mode 100644 data/adminer/fonts/entypo.woff create mode 100644 data/adminer/logo.png create mode 100644 data/compose.yaml create mode 100644 data/database-wikipedia-v2.js create mode 100644 data/database-wikipedia.js create mode 100755 data/database-wikipedia.sh create mode 100644 data/sql/0-insert-optimizer-start.sql create mode 100644 data/sql/1-pages-create.sql create mode 100644 data/sql/99-insert-optimizer-end.sql create mode 100644 data/test.js create mode 100644 data/utils.js diff --git a/.gitignore b/.gitignore index b5875ab..62337a4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ build/ .turbo bin/ cache.json +data/dump +data/sql/2-pages-inserts.sql # debug npm-debug.log* diff --git a/TODO.md b/TODO.md index 105dccd..c7974ed 100644 --- a/TODO.md +++ b/TODO.md @@ -2,11 +2,25 @@ - [x] chore: initial commit (+ mirror on GitHub) - [x] Deploy first staging version (v1.0.0-staging.1) +- [x] Wikipedia Database Dump + - [x] Download SQL files + - [x] Extract SQL files + - [x] Tables structure `CREATE TABLE` + - [x] `page.sql` (`pages` tables) + - [ ] `pagelinks.sql` (`internal_links` tables) + - [x] Adapt downloaded SQL files + - [x] `page.sql` (`pages` tables) + - [ ] `pagelinks.sql` (`internal_links` tables) + - [x] Import SQL files + - [ ] Verify file content up to before inserts, to check if it matches last version, and diff with last version + - [ ] Try `SELECT count(*) FROM internal_links il WHERE il.source_id = (SELECT p.id FROM pages WHERE p.title = 'Linux'); -- Count of internal links for 'Linux' page` + - [ ] Move from POC (Proof of concept) in `data` folder to `apps/cli` folder + - [ ] `.gitignore` correctly + Documentation how to use + Last execution date +- [ ] Implement REST API (`api`) with JSON responses ([AdonisJS](https://adonisjs.com/)) - [ ] Implement Wikipedia Game Solver (`website`) with inputs, button to submit, and list all pages to go from one to another, or none if it is not possible -- [ ] Check, cache and store (in `.json` file) all Wikipedia Pages and its internal links, maybe use Wikipedia Dump ()? +- [ ] Check how to deal with redirects (+ Wikipedia Database Dump related) - [ ] Implement toast notifications for errors, warnings, and success messages - [ ] Implement CLI (`cli`) -- [ ] Implement REST API (`api`) with JSON responses ([AdonisJS](https://adonisjs.com/)) - [ ] Add docs to add locale/edit translations, create component, install a dependency in a package, create a new package, technology used, architecture, links where it's deployed, how to use/install for end users, how to update dependencies with `npx taze -l` etc. ## Links diff --git a/data/.env.example b/data/.env.example new file mode 100644 index 0000000..83db903 --- /dev/null +++ b/data/.env.example @@ -0,0 +1,3 @@ +DATABASE_USER=wikipedia_user +DATABASE_PASSWORD=password +DATABASE_NAME=wikipedia diff --git a/data/.eslintrc.json b/data/.eslintrc.json new file mode 100644 index 0000000..42c084e --- /dev/null +++ b/data/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "root": true, + "extends": ["@repo/eslint-config"] +} diff --git a/data/README.md b/data/README.md index 2509542..88d3c29 100644 --- a/data/README.md +++ b/data/README.md @@ -1,17 +1,127 @@ # Wikipedia data -Database layout: +## Utils + +Show the first 10 line of sql file: `head -n 10 ./dump/page.sql` + +To inspect volume size used by database: `docker system df -v | grep 'wikipedia-solver-postgres-data'` + +## PostgreSQL related + +### Import SQL file to PostgreSQL Docker Container + +In `compose.yaml`, we can specify SQL scripts to be executed when the container starts for the first time. + +```yaml +volumes: + - "./sql:/docker-entrypoint-initdb.d/" +``` + +### Remove a volume + +```sh +# List all volumes +docker volume ls + +# Remove a volume +docker volume rm data_wikipedia-solver-postgres-data +``` + +## MySQL Related - +MySQL any way to import a huge (32 GB) sql dump faster?: + +Import data.sql MySQL Docker Container: + + ## Dumps Links +- Database layout: +- - -- -- -- -- -- +## `page.sql.gz` + +### MySQL full version + +```sql +-- MariaDB dump 10.19 Distrib 10.5.23-MariaDB, for debian-linux-gnu (x86_64) +-- +-- Host: db1206 Database: enwiki +-- ------------------------------------------------------ +-- Server version 10.6.17-MariaDB-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `page` +-- + +DROP TABLE IF EXISTS `page`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `page` ( + `page_id` int(8) unsigned NOT NULL AUTO_INCREMENT, + `page_namespace` int(11) NOT NULL DEFAULT 0, + `page_title` varbinary(255) NOT NULL DEFAULT '', + `page_is_redirect` tinyint(1) unsigned NOT NULL DEFAULT 0, + `page_is_new` tinyint(1) unsigned NOT NULL DEFAULT 0, + `page_random` double unsigned NOT NULL DEFAULT 0, + `page_touched` binary(14) NOT NULL, + `page_links_updated` varbinary(14) DEFAULT NULL, + `page_latest` int(8) unsigned NOT NULL DEFAULT 0, + `page_len` int(8) unsigned NOT NULL DEFAULT 0, + `page_content_model` varbinary(32) DEFAULT NULL, + `page_lang` varbinary(35) DEFAULT NULL, + PRIMARY KEY (`page_id`), + UNIQUE KEY `page_name_title` (`page_namespace`,`page_title`), + KEY `page_random` (`page_random`), + KEY `page_len` (`page_len`), + KEY `page_redirect_namespace_len` (`page_is_redirect`,`page_namespace`,`page_len`) +) ENGINE=InnoDB AUTO_INCREMENT=77490241 DEFAULT CHARSET=binary ROW_FORMAT=COMPRESSED; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `page` +-- + +/*!40000 ALTER TABLE `page` DISABLE KEYS */; +INSERT INTO `page` VALUES (10,0,'AccessibleComputing',1,0,0.856935107283,'20240722211426','20240722220435',1219062925,111,'wikitext',NULL); + +INSERT INTO `page` VALUES (10,0,'AccessibleComputing',1,0,0.856935107283,'20240722211426','20240722220435',1219062925,111,'wikitext',NULL),(12,0,'Anarchism',0,0,0.786172332974311,'20240731234111','20240731234202',1234495258,110759,'wikitext',NULL),(12281,0,'Gottfried_Wilhelm_Leibniz',0,0,0.79151204115852,'20240731234133','20240731234228',1237687724,155319,'wikitext',NULL),(13,0,'AfghanistanHistory',1,0,0.154661929211,'20240729123940','20240722220436',783865149,90,'wikitext',NULL),(14,0,'AfghanistanGeography',1,0,0.952234464653055,'20240722211426','20240722220436',783865160,92,'wikitext',NULL),(15,0,'AfghanistanPeople',1,0,0.047716566551,'20240722211426','20240722220436',783865293,95,'wikitext',NULL),(12473,1,'Gnosticism',0,0,0.00653186720472934,'20240801075011','20240731232236',1233717868,6579,'wikitext',NULL); +-- Expected output: INSERT INTO `page` VALUES (12,'Anarchism'),(12281,'Gottfried_Wilhelm_Leibniz'); +``` + +### PostgreSQL short version + +```sql +CREATE TABLE IF NOT EXISTS pages ( + id BIGSERIAL PRIMARY KEY, + title VARCHAR(255) UNIQUE NOT NULL + + -- is_redirect BOOLEAN NOT NULL DEFAULT FALSE +); + +-- Examples of inserts +INSERT INTO pages VALUES (10, 'AccessibleComputing'); -- (is_redirect = true) +INSERT INTO pages VALUES (10474, 'Eight_queens_puzzle'); -- (is_redirect = false) + +INSERT INTO pages VALUES +(10,'AccessibleComputing'), +(12,'Anarchism'), +(13,'AfghanistanHistory'), +(14,'AfghanistanGeography'), +(15,'AfghanistanPeople'); +``` diff --git a/data/adminer/default-orange.css b/data/adminer/default-orange.css new file mode 100644 index 0000000..4b7f66b --- /dev/null +++ b/data/adminer/default-orange.css @@ -0,0 +1,1376 @@ +a, +abbr, +acronym, +address, +applet, +aside, +b, +big, +blockquote, +body, +caption, +center, +cite, +code, +dd, +del, +dfn, +div, +dl, +dt, +em, +fieldset, +font, +footer, +form, +h1, +h2, +h3, +h4, +h5, +h6, +header, +html, +i, +iframe, +img, +ins, +kbd, +label, +legend, +li, +menu, +object, +ol, +p, +pre, +q, +s, +samp, +small, +span, +strike, +strong, +sub, +sup, +table, +tbody, +td, +tfoot, +th, +thead, +tr, +tt, +u, +ul, +var { + margin: 0; + padding: 0; + outline: 0; + border: none; + background: 0 0; + font-size: 10pt; + font-weight: 400; +} + +ol, +ul { + list-style: none; +} + +blockquote, +q { + quotes: none; +} + +blockquote:after, +blockquote:before, +q:after, +q:before { + content: none; +} + +:focus { + outline: 0; +} + +ins { + text-decoration: none; +} + +del { + text-decoration: line-through; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +aside, +menu { + display: block; +} + +input[type="submit"], +input[type="checkbox"], +input[type="radio"], +input[type="file"], +label, +select { + cursor: pointer; +} + +input[disabled=""] { + opacity: 0.5; + cursor: not-allowed; + color: #666 !important; + border-color: #aaa !important; +} + +input[type="text"] { + -webkit-user-modify: read-write-plaintext-only; +} + +@font-face { + font-family: entypo; + src: url(../fonts/entypo.eot); + src: + url(../fonts/entypo.eot?#iefix) format("embedded-opentype"), + url(../fonts/entypo.woff) format("woff"), + url(../fonts/entypo.ttf) format("truetype"), + url(../fonts/entypo.svg#entypo) format("svg"); + font-weight: 400; + font-style: normal; +} + +html { + overflow-y: scroll; + -webkit-text-size-adjust: none; +} + +body { + font-family: "Helvetica Neue", Helvetica, Verdana, Arial, sans-serif; + background: #fff; + color: #444; +} + +a, +a:visited { + padding: 4px 0; + color: #d55d00; + transition: + color 0.1s ease 0s, + background-color 0.1s ease 0s; +} + +a:link:hover, +a:visited:hover { + color: #aa4a00; + text-decoration: none; +} + +a sup { + padding: 0 5px; +} + +#logins a, +#tables a, +#tables span { + background: inherit; +} + +.active:before { + font-weight: 400; +} + +label { + padding: 3px 10px; +} + +input::-webkit-input-placeholder { + color: #999; +} + +input::-moz-placeholder { + color: #999; +} + +input:-ms-input-placeholder { + color: #999; +} + +input:not([type]), +input[type="text"], +input[type="email"], +input[type="password"], +input[type="search"], +input[type="number"], +pre[contenteditable="true"], +select, +textarea { + padding: 4px 5px !important; + border: 1px solid #ccc !important; + border-radius: 2px; + font-size: 10pt; + background: #fff; + color: #444; + box-shadow: inset 0 2px 2px #ebebeb; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +input:not([type]), +input[type="text"], +input[type="email"], +input[type="password"], +input[type="search"], +input[type="number"], +textarea { + -webkit-appearance: none; +} + +input:not([type]), +input[type="text"], +input[type="email"], +input[type="password"], +input[type="search"], +input[type="number"], +select { + height: 28px; +} + +input[type="submit"] { + display: inline-block; + padding: 7px 15px; + border: 1px solid #d55d00; + border-radius: 2px; + background: #d55d00; + color: #fff; + text-align: center; + text-decoration: none; + font-size: 10pt; + transition: background-color 0.1s ease 0s; + -webkit-appearance: none; +} + +input[type="submit"]:hover { + background: #aa4a00; + border-color: #aa4a00; +} + +input[type="submit"][disabled=""]:hover { + background: #fff; +} + +input[type="submit"].default { + box-shadow: none; +} + +input[type="image"] { + border: 4px solid #fff; + outline: 1px solid #f2ceb3; + -moz-outline-radius: 2px; + margin-right: 5px; +} + +input[type="image"]:last-child { + margin-right: 0; +} + +input[type="image"]:hover { + border-color: #fbefe6; +} + +input[type="checkbox"], +input[type="radio"] { + margin: 7px 5px 7px 0; +} + +fieldset { + margin: 5px 5px 10px 0; + padding: 5px 10px; + border: 1px solid #ddd; + border-radius: 2px; + background: #f6f6f6; + min-height: 55px; +} + +fieldset input[type="submit"] { + padding: 3px 10px; + border-color: #e69e66; + background: #fff; + color: #d55d00; +} + +fieldset input[type="submit"]:hover { + background: #fbefe6; + color: #d55d00; +} + +fieldset input[type="submit"].default { + border-color: #d55d00; + background: #d55d00; + color: #fff; +} + +fieldset input[type="submit"].default:hover { + background: #aa4a00; + border-color: #aa4a00; +} + +fieldset + table, +table + fieldset { + margin-top: 10px; +} + +fieldset legend a { + position: relative; + padding-bottom: 50px; +} + +fieldset > div > a, +fieldset > div > code, +fieldset > div > div, +fieldset > div > input, +fieldset > div > p, +fieldset > div > select { + position: relative; +} + +legend { + margin-bottom: 3px; +} + +fieldset input, +fieldset select, +p input, +p label, +p select { + margin: 0 5px 5px 0; +} + +.js fieldset > .hidden { + display: block; + margin-top: 5px; + text-align: center; +} + +.js fieldset > .hidden * { + display: none !important; +} + +.js fieldset > .hidden:before { + content: "โถ"; + font-family: entypo, sans-serif; + font-size: 40pt; + line-height: 0; + vertical-align: middle; + color: #e2e2e2; +} + +#fieldset-select.hidden:before { + content: "โš"; +} + +#fieldset-search.hidden:before { + content: "๐Ÿ”"; +} + +#fieldset-sort.hidden:before { + content: "โท"; +} + +#fieldset-export.hidden:before { + content: "๐Ÿ“ค"; +} + +#fieldset-import.hidden:before { + content: "๐Ÿ“ฅ"; +} + +#fieldset-history.hidden:before { + content: "๎ €"; +} + +#fieldset-history br { + display: block; + margin-bottom: 20px; +} + +#fieldset-history.hidden br { + display: none; +} + +#fieldset-partition.hidden:before { + content: "๎œฃ"; +} + +.size { + width: 8ex; +} + +.sqlarea { + max-width: 100%; + width: auto !important; + height: 350px !important; +} + +@media only screen and (max-width: 768px) { + input:not([type]), + input[type="text"], + input[type="email"], + input[type="password"], + input[type="search"], + input[type="number"], + pre[contenteditable="true"], + select, + textarea { + font-size: 12pt; + vertical-align: -1px; + } + + input:not([type]), + input[type="text"], + input[type="email"], + input[type="password"], + input[type="search"], + input[type="number"], + select { + height: 32px; + } + + fieldset input[type="submit"] { + padding: 6px 15px; + } + + .sqlarea { + height: 250px !important; + } +} + +@media only screen and (max-width: 360px) { + input:not([type]), + input[type="text"], + input[type="email"], + input[type="password"], + input[type="search"], + input[type="number"], + pre[contenteditable="true"], + select, + textarea { + width: 100%; + } + + fieldset input[type="submit"], + input[type="submit"] { + padding-left: 10px; + padding-right: 10px; + } +} + +#lang { + position: fixed; + right: 0; + top: 0; + left: auto; + border: none; + padding: 0 0 0 10px; + width: 190px; + height: 40px; + line-height: 30px; + font-size: 0; + z-index: 101; + background: #f6f6f6; +} + +#lang select { + padding: 2px 3px; + margin: 6px 0; + width: 100px; +} + +.logout { + position: fixed; + right: 10px; + margin: 0; + z-index: 101; + overflow: hidden; +} + +.logout input[type="submit"] { + border: none; + margin: 0; + padding: 0 10px; + height: 40px; + background: 0 0; + color: #d55d00; +} + +.logout input[type="submit"]:hover { + background: 0 0; + color: #aa4a00; +} + +@media only screen and (max-width: 768px) { + #lang { + position: static; + left: 0; + top: 0; + width: auto; + border-top: 1px solid #ddd; + background: #f6f6f6; + } + + #lang select { + margin: 4px 10px 0 10px; + } + + .logout { + position: relative; + float: right; + margin-top: -40px; + } +} + +@media only screen and (max-width: 360px) { + #lang select { + margin-left: 0; + } +} + +#content { + position: relative; + margin: 0 0 0 261px; + padding: 41px 20px 80px 20px; +} + +#content:before { + position: fixed; + left: 0; + top: 0; + content: ""; + display: block; + width: 100%; + height: 40px; + background: #f6f6f6; + border-bottom: 1px solid #ddd; +} + +#content .links + p { + color: #999; +} + +#breadcrumb { + position: fixed; + left: 261px; + top: 0; + right: 0; + margin: 0; + padding: 0 0 0 20px; + border-right: 205px solid #f6f6f6; + background: #f6f6f6; + height: 40px; + line-height: 40px; + z-index: 100; + overflow: hidden; + text-overflow: ellipsis; +} + +#breadcrumb a { + display: inline-block; + padding: 0; + height: 40px; + line-height: 40px; +} + +h2 { + margin: 20px 0; + font-size: 20pt; + color: #444; +} + +h3 { + margin: 30px 0 10px 0; + font-size: 16pt; +} + +p { + margin: 10px 0; +} + +code { + display: block; + padding: 10px; + margin: 5px 0; + border-left: 7px solid #cde; + border-radius: 2px; + background: #e8f0fa; + overflow: auto; +} + +fieldset code:first-child, +td code, +th code { + display: inline; + margin: 0; + padding: 0; + background: 0 0; + border: none; +} + +pre code { + margin: 0; +} + +fieldset code + i { + display: none; +} + +.time { + margin-left: 10px; + float: right; + font-size: 8pt; + color: #bbb; +} + +.error, +.message { + margin: 20px 0; + padding: 10px; + border-left: 7px solid #cec; +} + +.error { + color: #900; + border-color: #ecc; +} + +.message pre { + margin: 15px 0 5px 0; +} + +.message p { + margin: 0 0 5px 0; +} + +pre + .error, +pre + .message { + margin-top: 0; +} + +#help { + z-index: 200; + border: 1px solid #ddd; + border-radius: 2px; + background: #f6f6f6; + padding: 5px 7px; +} + +.icon { + background-color: #d55d00; +} + +.icon:hover { + background-color: #aa4a00; +} + +@media only screen and (max-width: 768px) { + #content { + margin-left: 0; + } + + #breadcrumb { + left: 0; + padding-left: 50px; + border-right-width: 0; + } +} + +@media only screen and (max-width: 360px) { + #content { + padding: 41px 10px 20px; + } + + h2 { + margin: 15px 0; + font-size: 16pt; + } +} + +h1 { + height: 40px; + white-space: nowrap; + overflow: hidden; +} + +h1 #h1 { + display: inline-block; + padding: 0; + background: url(logo.png) 10px center no-repeat; + background-size: 120px; + text-indent: -100px; + width: 135px; + height: 40px; +} + +#version, +.version { + position: relative; + top: -7px; + vertical-align: bottom; + font-size: 8pt; + font-style: italic; + color: #bbb; +} + +#version { + padding: 5px; + color: #d55d00; +} + +#version:hover { + color: #aa4a00; +} + +#menu { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: 260px; + margin: 0; + padding: 0; + border-right: 1px solid #ddd; + overflow-y: auto; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + background: #fff; + z-index: 100; +} + +#menu #dbs { + border-top: 1px solid #ddd; + border-bottom: none; + padding: 10px; + background: #f6f6f6; + color: #f6f6f6; + font-size: 0; +} + +#menu #dbs span { + font-size: 0; +} + +#menu #dbs select { + margin: 0; + width: 100%; +} + +#menu .links { + background: #f6f6f6; + border-bottom: 1px solid #ddd; + padding: 0 10px 7px 10px; +} + +#menu #logins, +#menu #tables { + border-bottom: 1px solid #ddd; + padding: 0; + margin-bottom: 25px; +} + +#menu .message { + background: 0 0; + border: none; + border-bottom: 1px solid #ddd; + color: #bbb; +} + +.menu-link { + display: block; + padding: 2px 10px; + width: auto; + height: 20px; + line-height: 20px; + color: #444; + overflow: hidden; + text-overflow: ellipsis; +} + +.menu-link.active { + color: #d55d00; + font-weight: 700; +} + +#tables a[href*="&table="] { + display: block; + padding: 2px 10px; + width: auto; + height: 20px; + line-height: 20px; + color: #444; + overflow: hidden; + text-overflow: ellipsis; + padding-right: 0; +} + +#tables a[href*="&table="].active { + color: #d55d00; + font-weight: 700; +} + +#tables a[href*="&table="]:hover { + background: #eee; +} + +#tables a[href*="&select="] { + float: right; + display: block; + padding: 2px 7px; + height: 20px; + line-height: 20px; + color: #999 !important; + overflow: hidden; + width: 16px; +} + +#tables a[href*="&select="]:hover { + color: #444 !important; +} + +#tables a[href*="&select="]:before { + content: "๐Ÿ“„ย ย "; + font-family: entypo, sans-serif; + font-size: 24pt; + line-height: 0; + vertical-align: -3px; +} + +#tables li:first-of-type a { + padding-top: 7px; +} + +#tables li:last-child a { + padding-bottom: 7px; +} + +#tables a.active + a { + color: #d55d00; + font-weight: 700; +} + +#tables br { + display: none; +} + +#tables.simple a { + display: block; + padding: 2px 10px; + width: auto; + height: 20px; + line-height: 20px; + color: #444; + overflow: hidden; + text-overflow: ellipsis; +} + +#tables.simple a.active { + color: #d55d00; + font-weight: 700; +} + +#tables.simple a[href*="&select="] { + display: block; + padding: 2px 10px; + width: auto; + height: 20px; + line-height: 20px; + color: #444; + overflow: hidden; + text-overflow: ellipsis; + float: none; + color: #444 !important; +} + +#tables.simple a[href*="&select="].active { + color: #d55d00; + font-weight: 700; +} + +#tables.simple a[href*="&select="].active { + color: #d55d00 !important; +} + +#tables.simple a[href*="&select="]:before { + content: ""; +} + +#tables.simple li:hover { + background: #eee; +} + +#tables.simple li:first-child a { + padding-top: 7px; +} + +#tables.simple li:last-child a { + padding-bottom: 7px; +} + +#logins { + border-top: 1px solid #ddd; +} + +#logins a { + display: block; + padding: 2px 10px; + width: auto; + height: 20px; + line-height: 20px; + color: #444; + overflow: hidden; + text-overflow: ellipsis; +} + +#logins a.active { + color: #d55d00; + font-weight: 700; +} + +#logins a:hover { + background: #eee; +} + +#logins a:first-of-type { + padding-top: 7px; +} + +#logins a:last-of-type { + padding-bottom: 7px; +} + +#logins br { + display: none; +} + +@media only screen and (max-width: 768px) { + h1:before { + float: left; + position: relative; + left: 4px; + top: 4px; + width: 30px; + height: 30px; + content: "โ˜ฐ"; + font-family: entypo, sans-serif; + font-size: 32pt; + line-height: 30px; + border: 1px solid #e69e66; + border-radius: 2px; + text-align: center; + vertical-align: middle; + background: #fff; + cursor: pointer; + } + + h1 #h1 { + margin-left: 10px; + } + + #menu { + width: 40px; + height: 40px; + bottom: auto; + border: none; + overflow: hidden; + background: 0 0; + } + + #menu form, + #menu p { + display: none; + } + + #menu.open { + width: 260px; + height: auto; + max-width: 100%; + max-height: 100%; + border-right: 1px solid #ddd; + border-bottom: 5px solid #ddd; + background: #fff; + box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.03); + z-index: 200; + overflow-y: auto; + } + + #menu.open form, + #menu.open p { + display: block; + } +} + +@media only screen and (max-width: 270px) { + #menu.open { + border-right: none; + } +} + +@media only screen and (-webkit-min-device-pixel-ratio: 1.5), + only screen and (min--moz-device-pixel-ratio: 1.5), + only screen and (-o-min-device-pixel-ratio: 3/2), + only screen and (min-device-pixel-ratio: 1.5) { + h1 #h1 { + background-image: url(../images/logo-hres.png?3); + background-size: 120px; + } +} + +a[href*="&sql="]:before { + content: "โœŽ"; + padding: 0 5px; + font-family: entypo, sans-serif; + font-size: 24pt; + line-height: 10pt; + vertical-align: -3px; +} + +.links { + line-height: 22px; +} + +.links a:before { + content: "โดย "; + font-family: entypo, sans-serif; + font-size: 24pt; + line-height: 10pt; + vertical-align: -3px; +} + +.links a[href*="&sql="]:before { + content: "๎œท"; + margin-left: -4px; + margin-right: 3px; +} + +.links a[href*="&import="]:before { + content: "๐Ÿ“ฅย "; +} + +.links a[href*="&dump="]:before { + content: "๐Ÿ“คย "; +} + +.links a[href*="&create="]:before, +.links a[href*="&db="][href*="&database="]:before, +.links a[href*="&indexes="]:before { + content: "โœŽย "; +} + +.links a[href$="&create="]:before, +.links a[href$="&database="]:before, +.links a[href$="&indexes="]:before { + content: "โž•ย "; +} + +.links a[href*="&schema="]:before { + content: "๐Ÿ•ชย "; +} + +.links a[href*="&privileges="]:before { + content: "๐Ÿ‘ฅย "; +} + +.links a[href*="&view="]:before { + content: "๎œŠย "; +} + +.links a[href*="&procedure="]:before, +.links a[href*="&function="]:before { + content: "๎€ƒย "; +} + +.links a[href*="&event="]:before { + content: "๐Ÿ”ย "; +} + +.links a[href*="&edit="]:before { + content: "โŠ•ย "; +} + +.links a[href*="&table="]:before { + content: "โš™ย "; +} + +.links a[href*="&select="]:before { + content: "๐Ÿ“„ย "; +} + +.links a[href*="&processlist="]:before { + content: "๎€…ย "; +} + +.links a[href*="&status="]:before { + content: "๐Ÿ“ฟย "; +} + +.links a[href*="&variables="]:before { + content: "๎œ”ย "; +} + +.links a[href*="&user="]:before { + content: "๎œ€ย "; +} + +.links a[href*="&foreign="]:before, +.links a[href*="&trigger="]:before { + content: "โž•ย "; +} + +table { + border: 1px solid #ddd; + margin: 20px 0 10px 0; +} + +table label.block { + padding: 0; +} + +tr { + border-bottom: 1px dotted #ddd; +} + +td, +th { + padding: 4px 10px; +} + +td[align="right"] input[type="checkbox"], +td[align="right"] input[type="radio"], +th[style="text-align: right;"] input[type="checkbox"], +th[style="text-align: right;"] input[type="radio"] { + margin-right: 0; + margin-left: 5px; +} + +.js .checkable .checked td, +.js .checkable .checked th, +.odd td, +tbody tr:hover td, +tbody tr:hover th, +thead td, +thead th { + background: 0 0; +} + +thead tr { + background: #f6f6f6; + border-bottom: 1px solid #ddd; +} + +thead td, +thead th { + padding: 7px 10px; + background: 0 0; + text-align: left; +} + +tbody td, +tbody th { + vertical-align: top; +} + +tbody td[align="right"] { + text-align: right; +} + +tbody td[align="right"] label.block { + text-align: right; +} + +tbody th span { + padding-top: 4px; +} + +table.checkable .checked { + background: #fbefe6; +} + +table.checkable input[type="checkbox"], +table.checkable input[type="radio"] { + margin: 2px 5px 2px 0; +} + +table.checkable > thead a { + padding: 7px 0; +} + +table.checkable > thead input[type="checkbox"], +table.checkable > thead input[type="radio"] { + margin: 2px 5px 2px 0; +} + +table.checkable > tbody > tr:hover { + background: #f5f5f5; +} + +table.checkable > tbody > tr.checked:hover { + background: #f8e4d4; +} + +.footer { + position: relative; + padding: 0; +} + +.footer > p { + position: fixed; + left: 261px; + right: 0; + bottom: 0; + margin: 0; + padding: 0 10px; + border: none; + border-top: 1px solid #ddd; + background: #f6f6f6; + z-index: 102; + font-weight: 700; +} + +.footer > p a, +.footer > p label { + display: inline-block; + margin: 0; + padding: 0 10px; + height: 40px; + line-height: 40px; +} + +.js .column { + background: #fff; + padding: 0; + margin: -36px 0 0 -62px; + border: 1px solid #e49254; + border-radius: 2px; + z-index: 10; +} + +.js .column a { + display: inline-block; + padding: 0; + width: 30px; + height: 30px; + overflow: hidden; + vertical-align: middle; +} + +.js .column a:before { + display: inline-block; + width: 30px; + height: 30px; + line-height: 30px; + font-family: entypo, sans-serif; + font-size: 24pt; + text-align: center; + vertical-align: -3px; +} + +.js .column a:hover:before { + background: #fbefe6; +} + +.js .column a[href*="&select="]:before { + content: "โฌ‡"; +} + +.js .column a[href="#fieldset-search"]:before { + content: "๐Ÿ”"; +} + +@media only screen and (max-width: 768px) { + .footer > p { + position: static; + margin: -10px 0 10px 0; + border-top: none; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; + border-bottom: 1px solid #ddd; + } +} + +a.jush-custom:hover, +a.jush-help:hover { + color: inherit; + text-decoration: underline; +} + +.json { + border-color: #cde; + border-left: 7px solid #cde; + background: #e8f0fa; + margin: 5px 0 3px 0; +} + +.json tr { + border-bottom: 1px solid #cde; +} + +.json tr:last-child { + border-bottom: none; +} + +.json th { + border-right: 1px solid #cde; + vertical-align: top; +} + +.json code { + padding: 4px 10px; +} + +.json + textarea { + margin-top: 6px; +} + +a.json-icon { + background: 0 0; + text-indent: 0; +} + +a.json-icon:hover { + background: 0 0; +} + +a.json-icon:before { + display: inline-block; + width: 20px; + height: 18px; + line-height: 18px; + font-family: entypo, sans-serif; + font-size: 24pt; + vertical-align: -3px; + content: "โ–ธ"; +} + +a.json-icon.json-up { + background: 0 0; + text-indent: 0; +} + +a.json-icon.json-up:before { + content: "โ–พ"; +} + +a.json-link { + padding-left: 0; +} + +a.json-link:before { + width: 10px; +} + +a.json-link span { + color: inherit; +} diff --git a/data/adminer/fonts/entypo.eot b/data/adminer/fonts/entypo.eot new file mode 100644 index 0000000000000000000000000000000000000000..1940b6dfa9a9cc3f05f6acd5136e03396a0f8c2d GIT binary patch literal 21233 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 AV*mgE literal 0 HcmV?d00001 diff --git a/data/adminer/fonts/entypo.svg b/data/adminer/fonts/entypo.svg new file mode 100644 index 0000000..a02386a --- /dev/null +++ b/data/adminer/fonts/entypo.svg @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/adminer/fonts/entypo.ttf b/data/adminer/fonts/entypo.ttf new file mode 100644 index 0000000000000000000000000000000000000000..539ff2b2ac2076e582d7ccf4ea4d0fe26415e0ec GIT binary patch literal 36860 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 Va3X@-@EtE?PTq$TDvM$>120$r>ouN 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 literal 0 HcmV?d00001 diff --git a/data/adminer/logo.png b/data/adminer/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9956bba1602399060112fb1843260de51062cb08 GIT binary patch literal 1332 zcmV-410M%Up(pms^mB&MBpGIw?ZH&Evoyt*isZ4RDf0e&nf3awbxo(ZU zQ+BI^p3H=v%Y&ZGgr3ZUp3H-u&DC81f}F|JTmaQw0D_#!f1AdCnaD(Go_m+YKX0E! zX`pwL!$oYNa*w})o5^yIziy1aM`xc+aHevQzeZ}JO>m}Zh`CL1rBHRKMQfo^bf|8P zykvy7X^6O0c&k=-tW|fdft<-mZlqCls8xEZX^6RAf3s0_sAGe+R(Pygd#+l1u49C? zO?jqmjk{xmwqJm=QF^Uogtbw0sYq_5QFWw4Vgtm8+!(V{2NpGZroXUTi z$a|N@bCAGVe6L1qqEU6Jnoj6G0001UbW%=J03tKY?q(5pM7x>Ys)$vY5eU08RRheb zA{0EiVL>MS1|$n5SbC(9mV#H^sCS6{5|UD-5JqeKS?|hbAnyHXcBS~G*!x_bl?(XjH?zHFf*Dvsxp}kq(&+cuve1Q;oNhte&kV+X(cjiec z*dowTYhyqpWi}7vQp%7m5MAW6t=0=6BumlUjXQMavN>aHc z23_tIc6}_V#6?_H_dG-h4eo)oXD=TARM@v`2eU8+>iJ?lurk8NXGfi#SjD1&0xeP#jai*jI zISai>W^#Kwkg0Chckf?6ZD)kZ3nva%61LM5k!{59L5j$%ASp=;C1I_np7x(>O{aKu zpzmQ!J~bMRM|ZDZzD4WtO2XxP;i5>cGHM1q(&Zz{L5r5sVhCMLtE0XXmPZH)`VQ>3 zfA~`1_QlqOy@+7CIL2bC$&CRIS!FtkBCQNMD65b_tkZ-(Fwy?z$uEVg*T&neU!KSk z8Bz?%re9VQLj{kK9Wh7=h>ui<6a3grH24CRd}Qp-snb_Ry4m@Y*0GSj&`{MxJ1Q1Z z%^wq%c*hLMzmjs9=_q2n9ai5s{`24-Ib}C4ojZEiChSXLpGebVLMEcMnvj}CjI|Dx zc8nakC^dkQ(R#f;y7y3>aG*J1-u$BwN)1=EaH}P#R3t;Z&_&^S=0`@O#Qn2h4F=n* z2^R*;t7jfE^avyjNku}y%9255G%N-NxBn&_+237F*iZ(R^69G?S0vL0%#Cz=r2#KB qEVc}6+Pa6$3@2m@gcF9my89P2-5(qMr^0Li0000} WikipediaPages + * + * Object to store pages from Wikipedia: + * - Key: page title sanitized - The real title shown is this title with underscores (_) converted to spaces ( ). + * - Value: page id. + */ + +/** + * Function to clean the `page.sql` file by: + * - Removing all lines that don't start with `INSERT INTO...`. + * - Filter by keeping rows where `page_namespace` (2nd column) is equal to 0, and where `page_is_redirect` (4th column) is equal to false (0). + * - Only keep columns `page_id` (1st column) and `page_title` (3rd column). + * @returns {Promise} + */ +const cleanPagesSQL = async () => { + /** @type {WikipediaPages} */ + const wikipediaPages = {} + + const INSERT_INTO_START_INPUT = "INSERT INTO `page` VALUES " + const sqlInputPath = path.join(SQL_DUMP_PATH, "page.sql") + const sqlInputStat = await fs.promises.stat(sqlInputPath) + const sqlInputFileStream = fs.createReadStream(sqlInputPath, "utf-8") + + let isInsideInsert = false + let current = "" + let lastPercent = 0 + + return await new Promise((resolve, reject) => { + sqlInputFileStream + .on("data", (dataInput) => { + const bytesReadRatio = sqlInputFileStream.bytesRead / sqlInputStat.size + const bytesReadPercent = bytesReadRatio * 100 + + if (bytesReadPercent - lastPercent >= 1) { + console.log( + `Bytes read (${bytesReadPercent.toFixed(2)}%): ${sqlInputFileStream.bytesRead} / ${sqlInputStat.size}`, + ) + lastPercent = bytesReadPercent + } + + let data = current + dataInput + + if (!isInsideInsert) { + const lines = data.split("\n").filter((line) => { + return line.startsWith(INSERT_INTO_START_INPUT) + }) + const [line] = lines + if (line == null) { + sqlInputFileStream.close() + return reject(new Error(`No "${INSERT_INTO_START_INPUT}" found.`)) + } + isInsideInsert = true + const lineStripped = line.slice(INSERT_INTO_START_INPUT.length) + data = lineStripped + } + + const { rows, unCompleted } = extractRowsFromSQLValues(data) + current = unCompleted + + for (const row of rows) { + if (row.length !== 12) { + sqlInputFileStream.close() + console.error([row]) + return reject(new Error(`Invalid Row values.`)) + } + + const id = Number.parseInt(row[0] ?? "0", 10) + const namespace = row[1] ?? "" + const title = row[2] ?? "" + const isRedirect = row[3] === "1" + + if (namespace === "0" && !isRedirect) { + wikipediaPages[title] = id + } + } + }) + .on("error", (error) => { + return reject(error) + }) + .on("close", () => { + return resolve(wikipediaPages) + }) + }) +} + +const wikipediaPages = await cleanPagesSQL() + +const sqlOutputPath = path.join(SQL_OUTPUT_PATH, "2-pages-inserts.sql") +const INSERT_INTO_START_OUTPUT = "INSERT INTO pages VALUES " + +const wikipediaPagesString = Object.entries(wikipediaPages) + .map(([title, id]) => { + return `(${id},${title})` + }) + .join(",") + +await fs.promises.writeFile( + sqlOutputPath, + `${INSERT_INTO_START_OUTPUT}${wikipediaPagesString};`, + { encoding: "utf-8" }, +) + +// const sqlOutputFile = await fs.promises.open(sqlOutputPath, "w") +// await sqlOutputFile.appendFile(INSERT_INTO_START_OUTPUT) +// await sqlOutputFile.appendFile(`(${id},${title}),\n`, "utf-8") diff --git a/data/database-wikipedia.js b/data/database-wikipedia.js new file mode 100644 index 0000000..a41fa5c --- /dev/null +++ b/data/database-wikipedia.js @@ -0,0 +1,94 @@ +import fs from "node:fs" +import path from "node:path" +import { extractRowsFromSQLValues } from "./utils.js" + +const SQL_DUMP_PATH = path.join(process.cwd(), "dump") +const SQL_OUTPUT_PATH = path.join(process.cwd(), "sql") + +/** + * Function to clean the `page.sql` file by: + * - Removing all lines that don't start with `INSERT INTO...`. + * - Filter by keeping rows where `page_namespace` (2nd column) is equal to 0, and where `page_is_redirect` (4th column) is equal to false (0). + * - Only keep columns `page_id` (1st column) and `page_title` (3rd column). + */ +const cleanPagesSQL = async () => { + const INSERT_INTO_START_INPUT = "INSERT INTO `page` VALUES " + const INSERT_INTO_START_OUTPUT = "INSERT INTO pages VALUES\n" + const sqlInputPath = path.join(SQL_DUMP_PATH, "page.sql") + const sqlOutputPath = path.join(SQL_OUTPUT_PATH, "2-pages-inserts.sql") + + const sqlInputStat = await fs.promises.stat(sqlInputPath) + const sqlInputFileStream = fs.createReadStream(sqlInputPath, "utf-8") + const sqlOutputFile = await fs.promises.open(sqlOutputPath, "w") + await sqlOutputFile.appendFile(INSERT_INTO_START_OUTPUT) + + let isInsideInsert = false + let current = "" + let lastPercent = 0 + + return await new Promise((resolve, reject) => { + sqlInputFileStream + .on("data", async (dataInput) => { + const bytesReadRatio = sqlInputFileStream.bytesRead / sqlInputStat.size + const bytesReadPercent = bytesReadRatio * 100 + + if (bytesReadPercent - lastPercent >= 1) { + console.log( + `Bytes read (${bytesReadPercent.toFixed(2)}%): ${sqlInputFileStream.bytesRead} / ${sqlInputStat.size}`, + ) + lastPercent = bytesReadPercent + } + + /** + * @type {string} + */ + let data = current + dataInput + + if (!isInsideInsert) { + const lines = data.split("\n").filter((line) => { + return line.startsWith(INSERT_INTO_START_INPUT) + }) + const [line] = lines + if (line == null) { + sqlInputFileStream.close() + return reject(new Error(`No "${INSERT_INTO_START_INPUT}" found.`)) + } + isInsideInsert = true + const lineStripped = line.slice(INSERT_INTO_START_INPUT.length) + data = lineStripped + } + + const { rows, unCompleted } = extractRowsFromSQLValues(data) + current = unCompleted + + for (const row of rows) { + if (row.length !== 12) { + sqlInputFileStream.close() + console.error([row]) + return reject(new Error(`Invalid Row values.`)) + } + + const id = Number.parseInt(row[0] ?? "0", 10) + const namespace = row[1] ?? "" + const title = row[2] ?? "" + const isRedirect = row[3] === "1" + + if (namespace === "0" && !isRedirect) { + await sqlOutputFile.appendFile(`(${id},${title}),\n`, "utf-8") + } + } + }) + .on("error", async (error) => { + await sqlOutputFile.close() + return reject(error) + }) + .on("close", async () => { + console.log(`Cleaned "${sqlInputPath}" to "${sqlOutputPath}".`) + await sqlOutputFile.appendFile(";\n", "utf-8") + await sqlOutputFile.close() + return resolve() + }) + }) +} + +await cleanPagesSQL() diff --git a/data/database-wikipedia.sh b/data/database-wikipedia.sh new file mode 100755 index 0000000..d89c02d --- /dev/null +++ b/data/database-wikipedia.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +# Usage: ./database-wikipedia.sh +# Description: Download and extract Wikipedia database dumps. + +set -o errexit +set -o nounset +set -o pipefail + +DUMP_DIRECTORY="dump" +SQL_OUTPUT_DIRECTORY="sql" +DOWNLOAD_DATE="latest" +WIKIPEDIA_DUMP_URL="https://dumps.wikimedia.org/enwiki/${DOWNLOAD_DATE}/enwiki-${DOWNLOAD_DATE}-" + +mkdir --parents "${DUMP_DIRECTORY}" + +download_file() { + local filename="${1}" + local file_path_output="${DUMP_DIRECTORY}/${filename}" + local file_url="${WIKIPEDIA_DUMP_URL}${filename}" + + if [[ ! -f "${file_path_output}" ]]; then + echo "Downloading \"${filename}\" from \"${file_url}\"..." + wget --output-document="${file_path_output}" "${file_url}" + else + echo "File \"${filename}\" from \"${file_url}\" already exists." + fi +} + +# download_file "page.sql.gz" +# download_file "pagelinks.sql.gz" + +extract_file() { + local filename="${1}" + local file_path_input="${DUMP_DIRECTORY}/${filename}" + local file_path_output="${DUMP_DIRECTORY}/${filename%.gz}" + + if [[ ! -f "${file_path_output}" ]]; then + echo "Extracting \"${filename}\" to \"${file_path_output}\"..." + gzip --decompress "${file_path_input}" + + # `--keep` flag to keep the original file, not needed here. + # gzip --decompress --keep "${file_path_input}" + else + echo "File \"${filename}\" already extracted." + fi +} + +# extract_file "page.sql.gz" +# extract_file "pagelinks.sql.gz" + +# Function to clean the `page.sql` file by: +# - Removing all lines that don't start with `INSERT INTO...`. +# - Filter by keeping rows where `page_namespace` (2nd column) is equal to 0, and where `page_is_redirect` (4th column) is equal to 0. +# - Only keep columns `page_id` (1st column) and `page_title` (3rd column). +# - Replace 'INSERT INTO `page` VALUES' with 'INSERT INTO pages VALUES'. +# - Replace escape single quote `\'` in MySQL to the PostgreSQL version `''`. +# - Replace escape double quote `\"` in MySQL to the PostgreSQL version `"`. +# - Handle backslashes `\\` by replacing them with a single backslash `\` for PostgreSQL. +clean_pages_sql() { + local sql_input_file_directory="${1}" + local sql_input="${sql_input_file_directory}/page.sql" + local sql_output="${SQL_OUTPUT_DIRECTORY}/2-pages-inserts.sql" + + sed --quiet '/^INSERT INTO `page` VALUES (/p' "${sql_input}" | + grep -oP "INSERT INTO \`page\` VALUES \(.+?\);" | + sed 's/),(/)\n(/g' | + grep -P "\([0-9]+,0,'.*?',0" | + sed -E "s/^\(([0-9]+),0,'([^']*)',0.*\)$/\1,'\2'/" | + sed "s/\\\'/''/g" | # Replace escaped single quotes + sed 's/\\"/"/g' | # Replace escaped double quotes + sed 's/\\\\/\\/g' | # Replace double backslashes with a single backslash + awk 'BEGIN {print "INSERT INTO pages VALUES"} {print "(" $0 "),"}' | + sed '$ s/,$/;/g' >"$sql_output" + + echo "Cleaned \"${sql_input}\" to \"${sql_output}\"." +} + +# clean_pages_sql "${DUMP_DIRECTORY}" diff --git a/data/sql/0-insert-optimizer-start.sql b/data/sql/0-insert-optimizer-start.sql new file mode 100644 index 0000000..896385a --- /dev/null +++ b/data/sql/0-insert-optimizer-start.sql @@ -0,0 +1,4 @@ +SET AUTOCOMMIT = 0; +SET FOREIGN_KEY_CHECKS = 0; +SET UNIQUE_CHECKS = 0; +BEGIN; diff --git a/data/sql/1-pages-create.sql b/data/sql/1-pages-create.sql new file mode 100644 index 0000000..0bedf96 --- /dev/null +++ b/data/sql/1-pages-create.sql @@ -0,0 +1,9 @@ +CREATE TABLE `pages` ( + `id` INT(8) UNSIGNED NOT NULL AUTO_INCREMENT, + `title` VARBINARY(255) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY (`title`) +) ENGINE=InnoDB AUTO_INCREMENT=77490241 DEFAULT CHARSET=binary ROW_FORMAT=COMPRESSED; + +-- VARBINARY usage instead of VARCHAR explanation: +-- > War on varchar. Changed all occurrences of varchar(N) and varchar(N) binary to varbinary(N). varchars cause problems ("Invalid mix of collations" errors) on MySQL databases with certain configs, most notably the default MySQL config. diff --git a/data/sql/99-insert-optimizer-end.sql b/data/sql/99-insert-optimizer-end.sql new file mode 100644 index 0000000..e724937 --- /dev/null +++ b/data/sql/99-insert-optimizer-end.sql @@ -0,0 +1,4 @@ +COMMIT; +SET AUTOCOMMIT = 1; +SET FOREIGN_KEY_CHECKS = 1; +SET UNIQUE_CHECKS = 1; diff --git a/data/test.js b/data/test.js new file mode 100644 index 0000000..f9e8715 --- /dev/null +++ b/data/test.js @@ -0,0 +1,48 @@ +import { extractRowsFromSQLValues } from "./utils.js" + +console.log( + "output:", + extractRowsFromSQLValues("(1,'-)',0),(2,'Demographics_of_American_Samoa',0)"), +) + +console.log( + "output:", + extractRowsFromSQLValues( + `(1,'-d\\'ff)',0),(2,'Demographics_of_American_Samoa',0)`, + ), +) + +console.log( + "output:", + extractRowsFromSQLValues( + "(1,'-)',0),(2,'Demographics_of_American_Samoa',0),(11,'abc',ddf,123,43,'dff'", + ), +) + +console.log( + "output:", + extractRowsFromSQLValues( + "(1,'-)',0),(2,'Demographics_of_American_Samoa',0),(11", + ), +) + +console.log( + "output:", + extractRowsFromSQLValues( + "(1,'-)',0),(2,'Demographics_of_American_Samoa',0),(", + ), +) + +console.log( + "output:", + extractRowsFromSQLValues( + `(1,'-)',0),(2,'C๏ผš\\\\',1,0),(2,'Demographics_of_American_Samoa',0)`, + ), +) + +console.log( + "output:", + extractRowsFromSQLValues( + `(1,'-)',0),(2,'Good_Singin\\',_Good_Playin\\'',1,0),(2,'Demographics_of_American_Samoa',0)`, + ), +) diff --git a/data/utils.js b/data/utils.js new file mode 100644 index 0000000..48f77bf --- /dev/null +++ b/data/utils.js @@ -0,0 +1,63 @@ +/** + * Extracts rows from a string of values in a SQL INSERT INTO statement, where each row is a comma-separated list of values enclosed in parentheses, possibly with the last row incomplete. + * @param {string} input + * @returns {{rows: string[][], unCompleted:string}} + * @example extractRowsFromSQLValues("(1,'-)',0),(2,'Demographics_of_American_Samoa',0)") // { rows: [["1","'-)'","0"],["2","'Demographics_of_American_Samoa'","0"]], unCompleted: "" } + */ +export const extractRowsFromSQLValues = (input) => { + const rows = [] + let index = 0 + let unCompleted = "" + + while (index < input.length) { + if (input[index] === "(") { + const row = [] + index++ // Skip the opening '(' + let value = "" + let insideQuotes = false + let rowComplete = false + + while (index < input.length && !rowComplete) { + if (input[index] === "'") { + // An escaped quote is preceded by an odd number of backslashes. + let backslashCount = 0 + let backIndex = index - 1 + while (backIndex >= 0 && input[backIndex] === "\\") { + backslashCount++ + backIndex-- + } + if (backslashCount % 2 === 0) { + insideQuotes = !insideQuotes + } + } + + if (input[index] === "," && !insideQuotes) { + row.push(value) + value = "" + } else if (input[index] === ")" && !insideQuotes) { + row.push(value) + rows.push(row) + rowComplete = true + } else { + value += input[index] + } + index++ + } + + if (!rowComplete) { + // If row is not completed, save it to unCompleted + unCompleted = "(" + if (row.length > 0) { + unCompleted += row.join(",") + "," + value + } else if (value.length > 0) { + unCompleted += value + } + break + } + } else { + index++ + } + } + + return { rows, unCompleted } +}