diff --git a/ast/postgresql.ts b/ast/postgresql.ts index 83cf2836..3f0dd398 100644 --- a/ast/postgresql.ts +++ b/ast/postgresql.ts @@ -448,7 +448,7 @@ export type alter_table_stmt = AstStatement; export type alter_action_list = alter_action[]; -export type alter_action = ALTER_ADD_COLUMN | ALTER_ADD_CONSTRAINT | ALTER_DROP_COLUMN | ALTER_ADD_INDEX_OR_KEY | ALTER_ADD_FULLETXT_SPARITAL_INDEX | ALTER_RENAME | ALTER_ALGORITHM | ALTER_LOCK | ALTER_OWNER_TO | ALTER_COLUMN_DATA_TYPE | ALTER_COLUMN_DEFAULT | ALTER_COLUMN_NOT_NULL; +export type alter_action = ALTER_ADD_COLUMN | ALTER_ADD_CONSTRAINT | ALTER_DROP_CONSTRAINT | ALTER_DROP_COLUMN | ALTER_ADD_INDEX_OR_KEY | ALTER_ADD_FULLETXT_SPARITAL_INDEX | ALTER_RENAME | ALTER_ALGORITHM | ALTER_LOCK | ALTER_OWNER_TO | ALTER_COLUMN_DATA_TYPE | ALTER_COLUMN_DEFAULT | ALTER_COLUMN_NOT_NULL; @@ -482,6 +482,16 @@ export type ALTER_ADD_CONSTRAINT = { +export type ALTER_DROP_CONSTRAINT = { + action: 'drop'; + constraint: ident, + keyword: 'constraint', + resource: 'constraint', + type: 'alter'; + }; + + + export type ALTER_ADD_INDEX_OR_KEY = { action: 'add'; type: 'alter'; diff --git a/package-lock.json b/package-lock.json index e58406ee..425fbf37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "node-sql-parser", - "version": "5.3.11", + "version": "5.3.13", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "node-sql-parser", - "version": "5.3.11", + "version": "5.3.13", "license": "Apache-2.0", "dependencies": { "@types/pegjs": "^0.10.0", @@ -41,6 +41,9 @@ "rimraf": "^5.0.1", "source-map-support": "^0.5.19", "tinyify": "^4.0.0", + "ts-auto-guard": "^5.0.1", + "tsx": "^4.21.0", + "typescript": "^5.9.3", "vscode-mocha-hmr": "^1.0.0", "webpack": "^4.43.0", "webpack-cli": "^4.7.0", @@ -1719,6 +1722,448 @@ "node": ">=10.0.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2262,6 +2707,61 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, + "node_modules/@ts-morph/common": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.24.0.tgz", + "integrity": "sha512-c1xMmNHWpNselmpIqursHeOHHBTIsJLbB+NuovbTTRCNiTLEr/U9dbJ8qy0jd/O2x5pc3seWuOUN5R2IoOTp8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.2", + "minimatch": "^9.0.4", + "mkdirp": "^3.0.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@ts-morph/common/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@types/chai": { "version": "4.3.14", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.14.tgz", @@ -2316,6 +2816,20 @@ "resolved": "https://registry.npmjs.org/@types/pegjs/-/pegjs-0.10.6.tgz", "integrity": "sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw==" }, + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -2819,6 +3333,16 @@ "node": ">=0.10.0" } }, + "node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -4239,6 +4763,98 @@ "node": ">=4" } }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/chalk-template/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chalk-template/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk-template/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chardet": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", @@ -4418,6 +5034,13 @@ "node": ">= 0.12.0" } }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "dev": true, + "license": "MIT" + }, "node_modules/collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -4482,6 +5105,46 @@ "node": ">= 0.8" } }, + "node_modules/command-line-args": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", + "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "find-replace": "^5.0.2", + "lodash.camelcase": "^4.3.0", + "typical": "^7.2.0" + }, + "engines": { + "node": ">=12.20" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/command-line-usage": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -6080,6 +6743,48 @@ "es6-symbol": "^3.1.1" } }, + "node_modules/esbuild": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -7419,6 +8124,24 @@ "node": ">=6" } }, + "node_modules/find-replace": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -7785,6 +8508,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -12750,6 +13486,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -13929,6 +14675,20 @@ "string-width": "^2.1.1" } }, + "node_modules/table-layout": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" + }, + "engines": { + "node": ">=12.17" + } + }, "node_modules/table/node_modules/ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -14501,6 +15261,46 @@ "node": ">=0.10.0" } }, + "node_modules/ts-auto-guard": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ts-auto-guard/-/ts-auto-guard-5.0.1.tgz", + "integrity": "sha512-0s4zJfYJK2eko8VF2YFbL6zncaMwSwfI94n+FM+zrQXMjbvw1DovChVCNIJwGTGnl6RhQG2x1Q8bx/sMrTo0Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "command-line-args": "^6.0.0", + "command-line-usage": "^7.0.2", + "ts-morph": "^23.0.0", + "tsconfig": "^7.0.0" + }, + "bin": { + "ts-auto-guard": "lib/cli.js" + } + }, + "node_modules/ts-morph": { + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-23.0.0.tgz", + "integrity": "sha512-FcvFx7a9E8TUe6T3ShihXJLiJOiqyafzFKUO4aqIHDUCIvADdGNShcbc2W5PMr3LerXRv7mafvFZ9lRENxJmug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.24.0", + "code-block-writer": "^13.0.1" + } + }, + "node_modules/tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -14531,7 +15331,47 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { - "node": ">=4" + "node": ">=4" + } + }, + "node_modules/tsconfig/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tsconfig/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, "node_modules/tty-browserify": { @@ -14682,6 +15522,30 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/umd": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", @@ -15798,6 +16662,16 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrapjs": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", + "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", @@ -17247,6 +18121,188 @@ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, + "@esbuild/aix-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "dev": true, + "optional": true + }, + "@esbuild/openharmony-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "dev": true, + "optional": true + }, "@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -17659,6 +18715,44 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, + "@ts-morph/common": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.24.0.tgz", + "integrity": "sha512-c1xMmNHWpNselmpIqursHeOHHBTIsJLbB+NuovbTTRCNiTLEr/U9dbJ8qy0jd/O2x5pc3seWuOUN5R2IoOTp8A==", + "dev": true, + "requires": { + "fast-glob": "^3.3.2", + "minimatch": "^9.0.4", + "mkdirp": "^3.0.1", + "path-browserify": "^1.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true + } + } + }, "@types/chai": { "version": "4.3.14", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.14.tgz", @@ -17713,6 +18807,18 @@ "resolved": "https://registry.npmjs.org/@types/pegjs/-/pegjs-0.10.6.tgz", "integrity": "sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw==" }, + "@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", + "dev": true + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, "@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -18140,6 +19246,12 @@ "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", "dev": true }, + "array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "dev": true + }, "array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -19270,6 +20382,66 @@ "supports-color": "^5.3.0" } }, + "chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "requires": { + "chalk": "^4.1.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "chardet": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", @@ -19411,6 +20583,12 @@ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true }, + "code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "dev": true + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -19471,6 +20649,30 @@ "delayed-stream": "~1.0.0" } }, + "command-line-args": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", + "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", + "dev": true, + "requires": { + "array-back": "^6.2.2", + "find-replace": "^5.0.2", + "lodash.camelcase": "^4.3.0", + "typical": "^7.2.0" + } + }, + "command-line-usage": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", + "dev": true, + "requires": { + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" + } + }, "commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -20749,6 +21951,40 @@ "es6-symbol": "^3.1.1" } }, + "esbuild": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" + } + }, "escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -21807,6 +23043,13 @@ "pkg-dir": "^3.0.0" } }, + "find-replace": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", + "dev": true, + "requires": {} + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -22079,6 +23322,15 @@ "get-intrinsic": "^1.2.4" } }, + "get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "requires": { + "resolve-pkg-maps": "^1.0.0" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -25970,6 +27222,12 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -26947,6 +28205,16 @@ } } }, + "table-layout": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", + "dev": true, + "requires": { + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" + } + }, "tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -27359,6 +28627,54 @@ "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", "dev": true }, + "ts-auto-guard": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ts-auto-guard/-/ts-auto-guard-5.0.1.tgz", + "integrity": "sha512-0s4zJfYJK2eko8VF2YFbL6zncaMwSwfI94n+FM+zrQXMjbvw1DovChVCNIJwGTGnl6RhQG2x1Q8bx/sMrTo0Pg==", + "dev": true, + "requires": { + "command-line-args": "^6.0.0", + "command-line-usage": "^7.0.2", + "ts-morph": "^23.0.0", + "tsconfig": "^7.0.0" + } + }, + "ts-morph": { + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-23.0.0.tgz", + "integrity": "sha512-FcvFx7a9E8TUe6T3ShihXJLiJOiqyafzFKUO4aqIHDUCIvADdGNShcbc2W5PMr3LerXRv7mafvFZ9lRENxJmug==", + "dev": true, + "requires": { + "@ts-morph/common": "~0.24.0", + "code-block-writer": "^13.0.1" + } + }, + "tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "requires": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + } + } + }, "tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -27388,6 +28704,17 @@ } } }, + "tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "requires": { + "esbuild": "~0.27.0", + "fsevents": "~2.3.3", + "get-tsconfig": "^4.7.5" + } + }, "tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", @@ -27503,6 +28830,18 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true + }, + "typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", + "dev": true + }, "umd": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", @@ -28389,6 +29728,12 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, + "wordwrapjs": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", + "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", + "dev": true + }, "worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", diff --git a/package.json b/package.json index b6a74593..6c1fdc77 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,11 @@ "start": "webpack --config webpack.config.js", "build": "npm run lint && npm run compile && webpack --config webpack.config.js --mode production", "test": "npm run lint && mochapack --reporter-option maxDiffSize=1000000 \"test/**/*.spec.js\"", + "test:types:mysql": "npm run generate:guards:mysql && npx tsx --test test/types/mysql/*.spec.ts", + "test:types:mysql:single": "npm run generate:guards:mysql && npx tsx --test", "lint": "eslint src", "compile": "babel src -d lib", + "generate:guards:mysql": "ts-auto-guard --export-all --paths types.d.ts && mv types.guard.ts test/types/mysql/", "coverLocal": "cross-env NODE_ENV=coverage nyc --reporter=lcov --reporter=text npm run test", "cover:run": "cross-env NODE_ENV=coverage nyc --reporter=text-lcov npm run test", "cover": "npm run cover:run && nyc report --reporter=text-lcov | coveralls", @@ -82,6 +85,9 @@ "rimraf": "^5.0.1", "source-map-support": "^0.5.19", "tinyify": "^4.0.0", + "ts-auto-guard": "^5.0.1", + "tsx": "^4.21.0", + "typescript": "^5.9.3", "vscode-mocha-hmr": "^1.0.0", "webpack": "^4.43.0", "webpack-cli": "^4.7.0", diff --git a/pegjs/mysql.pegjs b/pegjs/mysql.pegjs index fe436a79..7f6bfae1 100644 --- a/pegjs/mysql.pegjs +++ b/pegjs/mysql.pegjs @@ -3334,8 +3334,8 @@ primary / cast_expr / case_expr / literal_basic - / column_ref / literal_numeric + / column_ref / param / LPAREN __ list:or_and_where_expr __ RPAREN { list.parentheses = true @@ -4018,34 +4018,23 @@ line_terminator = [\n\r] literal_numeric - = n:number { + = n:number !ident_part { if (n && n.type === 'bigint') return n return { type: 'number', value: n }; } number = int_:int frac:frac exp:exp { - const numStr = int_ + frac + exp - return { - type: 'bigint', - value: numStr - } + return int_ + frac + exp; } / int_:int frac:frac { - const numStr = int_ + frac - if (isBigInt(int_)) return { - type: 'bigint', - value: numStr - } - const fixed = frac.length >= 1 ? frac.length - 1 : 0 - return parseFloat(numStr).toFixed(fixed); + const numStr = int_ + frac; + const num = parseFloat(numStr); + if (num.toString() === numStr) return num; + return numStr; } / int_:int exp:exp { - const numStr = int_ + exp - return { - type: 'bigint', - value: numStr - } + return int_ + exp; } / int_:int { if (isBigInt(int_)) return { @@ -4399,7 +4388,7 @@ assign_stmt_list } assign_stmt - = va:(var_decl / without_prefix_var_decl) __ s: (KW_ASSIGN / KW_ASSIGIN_EQUAL) __ e:proc_expr { + = va:(var_decl / without_prefix_var_decl) __ s: (KW_ASSIGN / KW_ASSIGIN_EQUAL) __ e:expr { return { type: 'assign', left: va, @@ -4409,7 +4398,7 @@ assign_stmt } select_assign_stmt - = va:(var_decl / without_prefix_var_decl) __ s:KW_ASSIGN __ e:proc_expr { + = va:(var_decl / without_prefix_var_decl) __ s:KW_ASSIGN __ e:expr { return { type: 'assign', left: va, diff --git a/test/ast.spec.js b/test/ast.spec.js index 166d7f75..294179db 100644 --- a/test/ast.spec.js +++ b/test/ast.spec.js @@ -760,6 +760,41 @@ describe('AST', () => { expect(getParsedSql('SELECT -042')).equal('SELECT -42'); }); + it('should parse integer as number', () => { + const ast = parser.astify('SELECT 3'); + expect(ast.columns[0].expr.type).to.equal('number'); + expect(ast.columns[0].expr.value).to.equal(3); + }); + + it('should parse decimal as number', () => { + const ast = parser.astify('SELECT 2.2'); + expect(ast.columns[0].expr.type).to.equal('number'); + expect(ast.columns[0].expr.value).to.equal(2.2); + }); + + it('should parse scientific notation as number', () => { + const ast = parser.astify('SELECT 1e1'); + expect(ast.columns[0].expr.type).to.equal('number'); + expect(ast.columns[0].expr.value).to.equal('1e1'); + }); + + it('should parse very large scientific notation as number', () => { + const ast = parser.astify('SELECT 1e999'); + expect(ast.columns[0].expr.type).to.equal('number'); + expect(ast.columns[0].expr.value).to.equal('1e999'); + }); + + it('should parse very large integer as bigint', () => { + const ast = parser.astify('SELECT 222222222222222222222222'); + expect(ast.columns[0].expr.type).to.equal('bigint'); + expect(ast.columns[0].expr.value).to.equal('222222222222222222222222'); + }); + + it('should parse very large decimal as number with string value', () => { + const ast = parser.astify('SELECT 222222222222222222222222.2'); + expect(ast.columns[0].expr.type).to.equal('number'); + expect(ast.columns[0].expr.value).to.equal('222222222222222222222222.2'); + }); describe('datetime', () => { const literals = { diff --git a/test/cmd.spec.js b/test/cmd.spec.js index ee25307c..22528fe2 100644 --- a/test/cmd.spec.js +++ b/test/cmd.spec.js @@ -369,6 +369,11 @@ describe('Command SQL', () => { .to.equal(`SET @@${keyword}.id = 123 ; SET @@${keyword}.yy.xx = "abcd"`); }) }) + + it('should support set variable with cast', () => { + expect(getParsedSql('SET @myvar=CAST("123.1" AS DOUBLE)')) + .to.equal('SET @myvar = CAST("123.1" AS DOUBLE)'); + }) }) describe('unlock', () => { diff --git a/test/insert.spec.js b/test/insert.spec.js index ad8daf5f..ac461602 100644 --- a/test/insert.spec.js +++ b/test/insert.spec.js @@ -104,9 +104,12 @@ describe('insert', () => { }) it('should support big number', () => { - const bigNumberList = ['3511161156327326047123', '23.3e+12323243434'] + const bigNumberList = [ + { value: '3511161156327326047123', type: 'bigint' }, + { value: '23.3e+12323243434', type: 'number' } + ] for (const bigNumber of bigNumberList) { - const sql = `INSERT INTO t1(id) VALUES(${bigNumber})` + const sql = `INSERT INTO t1(id) VALUES(${bigNumber.value})` const ast = parser.astify(sql) expect(ast.values).to.be.eql({ type: 'values', @@ -115,15 +118,15 @@ describe('insert', () => { type: 'expr_list', value: [ { - type: 'bigint', - value: bigNumber + type: bigNumber.type, + value: bigNumber.value } ], prefix: null } ] }) - expect(parser.sqlify(ast)).to.equal('INSERT INTO `t1` (id) VALUES (' + bigNumber + ')') + expect(parser.sqlify(ast)).to.equal('INSERT INTO `t1` (id) VALUES (' + bigNumber.value + ')') } }) diff --git a/test/types/mysql/README.md b/test/types/mysql/README.md new file mode 100644 index 00000000..13a7123b --- /dev/null +++ b/test/types/mysql/README.md @@ -0,0 +1,112 @@ +# TypeScript Type Tests + +This directory contains TypeScript tests that verify the type definitions in `types.d.ts` match the actual runtime behavior of the SQL parser. + +## Type Guards + +Type guards are automatically generated using `ts-auto-guard` and stored in `types.guard.ts`. + +### Generating Guards + +To regenerate the type guards after modifying `types.d.ts`: + +```bash +npm run generate-guards +``` + +This will: +1. Run `ts-auto-guard` on `types.d.ts` +2. Move the generated `types.guard.ts` file to `test/types/` + +### Using Guards in Tests + +Import the guards you need from `types.guard.ts`: + +```typescript +import { isSelect, isUpdate, isDelete } from './types.guard.ts'; + +test('example', () => { + const ast = parser.astify(sql); + assert.ok(isSelect(ast), 'AST should be a Select type'); + // Now safely use ast as Select +}); +``` + +### Available Guards + +All exported types from `types.d.ts` have corresponding guard functions with the naming pattern `is{TypeName}`. + +Examples: +- `isSelect(obj)` - checks if obj is a Select type +- `isUpdate(obj)` - checks if obj is an Update type +- `isDelete(obj)` - checks if obj is a Delete type +- `isInsert_Replace(obj)` - checks if obj is an Insert_Replace type +- `isBinary(obj)` - checks if obj is a Binary type +- `isColumnRef(obj)` - checks if obj is a ColumnRef type + +## Running the Tests + +```bash +npx tsx --test test/types/*.spec.ts +``` + +Or use the shorthand: + +```bash +tsx --test test/types/**.spec.ts +``` + +## Test Files + +- `select.spec.ts` - Tests for SELECT statement types +- `insert.spec.ts` - Tests for INSERT statement types +- `update.spec.ts` - Tests for UPDATE statement types +- `delete.spec.ts` - Tests for DELETE statement types +- `expressions.spec.ts` - Tests for expression types (functions, aggregates, CASE, CAST) +- `joins.spec.ts` - Tests for JOIN types + +## How It Works + +The tests use a parser loader (`parser-loader.mjs`) that: +1. Loads the compiled MySQL parser from `build/mysql.js` +2. Wraps it in a simple Parser class +3. Extracts the AST from the parser result + +Each test: +1. Provides a SQL string +2. Parses it using the parser +3. Uses type guards to verify the AST type at runtime +4. Verifies the resulting AST matches the TypeScript type definitions + +## Adding New Tests + +To add new tests: + +1. Create a new `.spec.ts` file in this directory +2. Import the Parser from `./parser-loader.mjs` +3. Import the relevant types from `../../types.d.ts` +4. Import the relevant guards from `./types.guard.ts` +5. Write tests using Node.js test runner (`node:test`) +6. Use type guards to verify types at runtime + +Example: + +```typescript +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('My SQL test', () => { + const sql = 'SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + // Add more assertions... +}); +``` diff --git a/test/types/mysql/advanced-features.spec.ts b/test/types/mysql/advanced-features.spec.ts new file mode 100644 index 00000000..2c711e2f --- /dev/null +++ b/test/types/mysql/advanced-features.spec.ts @@ -0,0 +1,95 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, AST, WindowExpr, NamedWindowExpr, From } from '../../../types.d.ts'; +import { isSelect, isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('Multiple statements return AST array', () => { + const sql = 'SELECT 1; SELECT 2;'; + const ast = parser.astify(sql); + + assert.ok(Array.isArray(ast), 'Multiple statements should return an array'); + assert.strictEqual(ast.length, 2); + assert.ok(isSelect(ast[0])); + assert.ok(isSelect(ast[1])); +}); + +test('Named window expression in WINDOW clause', () => { + const sql = 'SELECT id, ROW_NUMBER() OVER w FROM t WINDOW w AS (ORDER BY id)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.ok(selectAst.window, 'Should have window clause'); + assert.strictEqual(selectAst.window.keyword, 'window'); + assert.strictEqual(selectAst.window.type, 'window'); + assert.ok(Array.isArray(selectAst.window.expr)); + + const namedWindow = selectAst.window.expr[0]; + assert.strictEqual(namedWindow.name, 'w'); + assert.ok(typeof namedWindow.as_window_specification === 'object'); + assert.ok('window_specification' in namedWindow.as_window_specification); +}); + +test('Window function references named window by string', () => { + const sql = 'SELECT ROW_NUMBER() OVER w FROM t WINDOW w AS (ORDER BY id)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const col = selectAst.columns[0]; + assert.strictEqual(col.expr.type, 'function'); + + if (col.expr.type === 'function') { + assert.ok(col.expr.over); + assert.strictEqual(col.expr.over.type, 'window'); + assert.strictEqual(typeof col.expr.over.as_window_specification, 'string'); + assert.strictEqual(col.expr.over.as_window_specification, 'w'); + } +}); + +test('Complex FROM with parentheses', () => { + const sql = 'SELECT * FROM (t1, t2)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.ok(typeof selectAst.from === 'object' && !Array.isArray(selectAst.from)); + + if (typeof selectAst.from === 'object' && !Array.isArray(selectAst.from) && 'expr' in selectAst.from) { + assert.ok(Array.isArray(selectAst.from.expr)); + assert.strictEqual(selectAst.from.expr.length, 2); + assert.ok('parentheses' in selectAst.from); + assert.strictEqual(selectAst.from.parentheses.length, 1); + assert.ok(Array.isArray(selectAst.from.joins)); + } +}); + +test('Set operation with UNION', () => { + const sql = 'SELECT 1 UNION SELECT 2'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.set_op, 'union'); + assert.ok(selectAst._next); + assert.ok(isSelect(selectAst._next)); +}); + +test('CREATE INDEX with algorithm and lock options', () => { + const sql = 'CREATE INDEX idx ON t (id) ALGORITHM = INPLACE LOCK = NONE'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as any; + assert.strictEqual(createAst.type, 'create'); + assert.strictEqual(createAst.keyword, 'index'); + assert.ok(createAst.algorithm_option); + assert.strictEqual(createAst.algorithm_option.keyword, 'algorithm'); + assert.strictEqual(createAst.algorithm_option.algorithm, 'INPLACE'); + assert.ok(createAst.lock_option); + assert.strictEqual(createAst.lock_option.keyword, 'lock'); + assert.strictEqual(createAst.lock_option.lock, 'NONE'); +}); diff --git a/test/types/mysql/aggr-func-properties.spec.ts b/test/types/mysql/aggr-func-properties.spec.ts new file mode 100644 index 00000000..f10e8c2a --- /dev/null +++ b/test/types/mysql/aggr-func-properties.spec.ts @@ -0,0 +1,83 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, AggrFunc } from '../../../types.d.ts'; +import { isSelect, isAggrFunc } from './types.guard.ts'; + +const parser = new Parser(); + +test('AggrFunc - with DISTINCT', () => { + const sql = 'SELECT COUNT(DISTINCT user_id) FROM orders'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + const col = select.columns[0]; + assert.ok(isAggrFunc(col.expr)); + const aggr = col.expr as AggrFunc; + assert.strictEqual(aggr.type, 'aggr_func'); + assert.strictEqual(aggr.args.distinct, 'DISTINCT'); +}); + +test('AggrFunc - without DISTINCT', () => { + const sql = 'SELECT COUNT(user_id) FROM orders'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + const col = select.columns[0]; + assert.ok(isAggrFunc(col.expr)); + const aggr = col.expr as AggrFunc; + assert.strictEqual(aggr.type, 'aggr_func'); + assert.ok(aggr.args.distinct === null || aggr.args.distinct === undefined); +}); + +test('AggrFunc - with ORDER BY (GROUP_CONCAT)', () => { + const sql = 'SELECT GROUP_CONCAT(name ORDER BY name ASC) FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + const col = select.columns[0]; + assert.ok(isAggrFunc(col.expr)); + const aggr = col.expr as AggrFunc; + assert.strictEqual(aggr.name, 'GROUP_CONCAT'); + assert.ok(aggr.args.orderby); + assert.ok(Array.isArray(aggr.args.orderby)); + assert.strictEqual(aggr.args.orderby.length, 1); +}); + +test('AggrFunc - with SEPARATOR (GROUP_CONCAT)', () => { + const sql = "SELECT GROUP_CONCAT(name SEPARATOR ', ') FROM users"; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + const col = select.columns[0]; + assert.ok(isAggrFunc(col.expr)); + const aggr = col.expr as AggrFunc; + assert.strictEqual(aggr.name, 'GROUP_CONCAT'); + assert.ok(aggr.args.separator); +}); + +test('AggrFunc - with DISTINCT, ORDER BY, and SEPARATOR', () => { + const sql = "SELECT GROUP_CONCAT(DISTINCT name ORDER BY name SEPARATOR ', ') FROM users"; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + const col = select.columns[0]; + assert.ok(isAggrFunc(col.expr)); + const aggr = col.expr as AggrFunc; + assert.strictEqual(aggr.name, 'GROUP_CONCAT'); + assert.strictEqual(aggr.args.distinct, 'DISTINCT'); + assert.ok(aggr.args.orderby); + assert.ok(aggr.args.separator); +}); + +test('AggrFunc - with OVER clause', () => { + const sql = 'SELECT SUM(amount) OVER (PARTITION BY user_id) FROM orders'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + const col = select.columns[0]; + assert.ok(isAggrFunc(col.expr)); + const aggr = col.expr as AggrFunc; + assert.ok(aggr.over); + assert.strictEqual(aggr.over.type, 'window'); +}); diff --git a/test/types/mysql/aggregate-functions.spec.ts b/test/types/mysql/aggregate-functions.spec.ts new file mode 100644 index 00000000..05a38663 --- /dev/null +++ b/test/types/mysql/aggregate-functions.spec.ts @@ -0,0 +1,58 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Column, AggrFunc } from '../../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('GROUP_CONCAT with separator', () => { + const sql = "SELECT GROUP_CONCAT(name SEPARATOR ', ') FROM users"; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const col = (selectAst.columns as Column[])[0]; + const aggrFunc = col.expr as AggrFunc; + assert.strictEqual(aggrFunc.type, 'aggr_func'); + assert.ok(aggrFunc.args.separator); + assert.strictEqual(typeof aggrFunc.args.separator, 'object'); +}); + +test('COUNT without DISTINCT or ORDER BY - check if properties exist', () => { + const sql = "SELECT COUNT(id) FROM users"; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const col = (selectAst.columns as Column[])[0]; + const aggrFunc = col.expr as AggrFunc; + assert.strictEqual(aggrFunc.type, 'aggr_func'); + assert.ok('distinct' in aggrFunc.args); + assert.ok('orderby' in aggrFunc.args); +}); + +test('COUNT with DISTINCT', () => { + const sql = "SELECT COUNT(DISTINCT id) FROM users"; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const col = (selectAst.columns as Column[])[0]; + const aggrFunc = col.expr as AggrFunc; + assert.strictEqual(aggrFunc.type, 'aggr_func'); + assert.strictEqual(aggrFunc.args.distinct, 'DISTINCT'); +}); + +test('GROUP_CONCAT with ORDER BY', () => { + const sql = "SELECT GROUP_CONCAT(name ORDER BY name) FROM users"; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const col = (selectAst.columns as Column[])[0]; + const aggrFunc = col.expr as AggrFunc; + assert.strictEqual(aggrFunc.type, 'aggr_func'); + assert.ok(aggrFunc.args.orderby); + assert.ok(Array.isArray(aggrFunc.args.orderby)); +}); diff --git a/test/types/mysql/alter.spec.ts b/test/types/mysql/alter.spec.ts new file mode 100644 index 00000000..76aad4ff --- /dev/null +++ b/test/types/mysql/alter.spec.ts @@ -0,0 +1,211 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Alter, AlterExpr } from '../../types.d.ts'; +import { isAlter } from './types.guard.ts'; + +const parser = new Parser(); + +test('ALTER TABLE - ADD COLUMN', () => { + const sql = 'ALTER TABLE users ADD COLUMN email VARCHAR(255)'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'add'); + assert.strictEqual(expr.keyword, 'COLUMN'); + assert.strictEqual(expr.resource, 'column'); + assert.ok(expr.column); + assert.ok(expr.definition); +}); + +test('ALTER TABLE - ADD COLUMN without COLUMN keyword', () => { + const sql = 'ALTER TABLE users ADD email VARCHAR(255)'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'add'); + assert.strictEqual(expr.resource, 'column'); + assert.ok(expr.column); + assert.ok(expr.definition); +}); + +test('ALTER TABLE - DROP COLUMN', () => { + const sql = 'ALTER TABLE users DROP COLUMN email'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'drop'); + assert.strictEqual(expr.keyword, 'COLUMN'); + assert.strictEqual(expr.resource, 'column'); + assert.ok(expr.column); +}); + +test('ALTER TABLE - DROP COLUMN without COLUMN keyword', () => { + const sql = 'ALTER TABLE users DROP email'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'drop'); + assert.strictEqual(expr.resource, 'column'); + assert.ok(expr.column); +}); + +test('ALTER TABLE - MODIFY COLUMN', () => { + const sql = 'ALTER TABLE users MODIFY COLUMN email TEXT'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'modify'); + assert.strictEqual(expr.keyword, 'COLUMN'); + assert.strictEqual(expr.resource, 'column'); +}); + +test('ALTER TABLE - CHANGE COLUMN', () => { + const sql = 'ALTER TABLE users CHANGE COLUMN old_email new_email VARCHAR(255)'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'change'); + assert.strictEqual(expr.keyword, 'COLUMN'); + assert.strictEqual(expr.resource, 'column'); +}); + +test('ALTER TABLE - RENAME TABLE', () => { + const sql = 'ALTER TABLE users RENAME TO customers'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'rename'); + assert.strictEqual(expr.resource, 'table'); + assert.strictEqual(expr.keyword, 'to'); +}); + +test('ALTER TABLE - RENAME COLUMN', () => { + const sql = 'ALTER TABLE users RENAME COLUMN old_name TO new_name'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'rename'); + assert.strictEqual(expr.resource, 'column'); + assert.strictEqual(expr.keyword, 'column'); +}); + +test('ALTER TABLE - ADD INDEX', () => { + const sql = 'ALTER TABLE users ADD INDEX idx_email (email)'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'add'); + assert.strictEqual(expr.resource, 'index'); + assert.strictEqual(expr.keyword, 'index'); +}); + +test('ALTER TABLE - DROP INDEX', () => { + const sql = 'ALTER TABLE users DROP INDEX idx_email'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'drop'); + assert.strictEqual(expr.resource, 'index'); + assert.strictEqual(expr.keyword, 'index'); +}); + +test('ALTER TABLE - DROP PRIMARY KEY', () => { + const sql = 'ALTER TABLE users DROP PRIMARY KEY'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'drop'); + assert.strictEqual(expr.resource, 'key'); + assert.strictEqual(expr.keyword, 'primary key'); +}); + +test('ALTER TABLE - DROP FOREIGN KEY', () => { + const sql = 'ALTER TABLE users DROP FOREIGN KEY fk_user'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'drop'); + assert.strictEqual(expr.resource, 'key'); + assert.strictEqual(expr.keyword, 'foreign key'); +}); + +test('ALTER TABLE - ADD CONSTRAINT', () => { + const sql = 'ALTER TABLE users ADD CONSTRAINT pk_id PRIMARY KEY (id)'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'add'); + assert.strictEqual(expr.resource, 'constraint'); + assert.ok(expr.create_definitions); +}); + +test('ALTER TABLE - DROP CONSTRAINT', () => { + const sql = 'ALTER TABLE users DROP CONSTRAINT chk_age'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'drop'); + assert.strictEqual(expr.resource, 'constraint'); + assert.strictEqual(expr.keyword, 'constraint'); +}); + +test('ALTER TABLE - DROP CHECK', () => { + const sql = 'ALTER TABLE users DROP CHECK chk_age'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'drop'); + assert.strictEqual(expr.resource, 'constraint'); + assert.strictEqual(expr.keyword, 'check'); +}); + +test('ALTER TABLE - ALGORITHM', () => { + const sql = 'ALTER TABLE users ALGORITHM = INPLACE, ADD COLUMN email VARCHAR(255)'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.type, 'alter'); + assert.strictEqual(expr.keyword, 'algorithm'); + assert.strictEqual(expr.resource, 'algorithm'); +}); + +test('ALTER TABLE - LOCK', () => { + const sql = 'ALTER TABLE users LOCK = NONE, ADD COLUMN email VARCHAR(255)'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.type, 'alter'); + assert.strictEqual(expr.keyword, 'lock'); + assert.strictEqual(expr.resource, 'lock'); +}); + +test('ALTER TABLE - ADD PARTITION', () => { + const sql = 'ALTER TABLE users ADD PARTITION (PARTITION p3 VALUES LESS THAN (2000))'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'add'); + assert.strictEqual(expr.resource, 'partition'); + assert.strictEqual(expr.keyword, 'PARTITION'); +}); + +test('ALTER TABLE - DROP PARTITION', () => { + const sql = 'ALTER TABLE users DROP PARTITION p0'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.action, 'drop'); + assert.strictEqual(expr.resource, 'partition'); + assert.strictEqual(expr.keyword, 'PARTITION'); +}); + +test('ALTER TABLE - table option ENGINE', () => { + const sql = 'ALTER TABLE users ENGINE = InnoDB'; + const ast = parser.astify(sql); + assert.ok(isAlter(ast)); + const expr = (ast as Alter).expr[0]; + assert.strictEqual(expr.type, 'alter'); + assert.strictEqual(expr.resource, 'engine'); +}); + diff --git a/test/types/mysql/binary-unary-exprlist.spec.ts b/test/types/mysql/binary-unary-exprlist.spec.ts new file mode 100644 index 00000000..1fb19c1d --- /dev/null +++ b/test/types/mysql/binary-unary-exprlist.spec.ts @@ -0,0 +1,104 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import { isSelect, isBinary, isExprList } from './types.guard.ts'; + +const parser = new Parser(); + +describe('Binary, Unary, and ExprList Types', () => { + test('Binary expression with AND operator', () => { + const ast = parser.astify("SELECT * FROM t WHERE a AND b"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.ok(isBinary(where), 'WHERE clause should be Binary'); + assert.strictEqual(where.operator, 'AND'); + }); + + test('Binary expression with OR operator', () => { + const ast = parser.astify("SELECT * FROM t WHERE a OR b"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.ok(isBinary(where), 'WHERE clause should be Binary'); + assert.strictEqual(where.operator, 'OR'); + }); + + test('Binary expression with comparison operators', () => { + const ast = parser.astify("SELECT * FROM t WHERE age > 18"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.ok(isBinary(where), 'WHERE clause should be Binary'); + assert.strictEqual(where.operator, '>'); + }); + + test('Binary expression with BETWEEN', () => { + const ast = parser.astify("SELECT * FROM t WHERE age BETWEEN 18 AND 65"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.ok(isBinary(where), 'WHERE clause should be Binary'); + assert.strictEqual(where.operator, 'BETWEEN'); + }); + + test('Binary expression with IS NULL', () => { + const ast = parser.astify("SELECT * FROM t WHERE name IS NULL"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.ok(isBinary(where), 'WHERE clause should be Binary'); + assert.strictEqual(where.operator, 'IS'); + }); + + test('Binary expression with IS NOT NULL', () => { + const ast = parser.astify("SELECT * FROM t WHERE name IS NOT NULL"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.ok(isBinary(where), 'WHERE clause should be Binary'); + assert.strictEqual(where.operator, 'IS NOT'); + }); + + test('Unary expression with NOT', () => { + const ast = parser.astify("SELECT * FROM t WHERE NOT active"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.strictEqual(where.type, 'unary_expr'); + assert.strictEqual(where.operator, 'NOT'); + assert.ok(where.expr, 'Should have expr property'); + }); + + test('ExprList in IN clause', () => { + const ast = parser.astify("SELECT * FROM t WHERE id IN (1, 2, 3)"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.ok(isBinary(where), 'WHERE clause should be Binary'); + assert.strictEqual(where.operator, 'IN'); + assert.ok(isExprList(where.right), 'Right side should be ExprList'); + assert.strictEqual(where.right.type, 'expr_list'); + assert.strictEqual(where.right.value.length, 3); + }); + + test('ExprList in function arguments', () => { + const ast = parser.astify("SELECT CONCAT(first_name, ' ', last_name) FROM users"); + assert.ok(isSelect(ast), 'Should be Select'); + const column = ast.columns[0]; + assert.strictEqual(column.expr.type, 'function'); + const args = column.expr.args; + assert.ok(isExprList(args), 'Function args should be ExprList'); + assert.strictEqual(args.value.length, 3); + }); + + test('Binary expression with nested structure', () => { + const ast = parser.astify("SELECT * FROM t WHERE (a AND b) OR c"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.ok(isBinary(where), 'WHERE clause should be Binary'); + assert.strictEqual(where.operator, 'OR'); + assert.ok(isBinary(where.left), 'Left side should be Binary'); + assert.strictEqual(where.left.operator, 'AND'); + }); + + test('EXISTS is a Function type', () => { + const ast = parser.astify("SELECT * FROM t WHERE EXISTS (SELECT 1 FROM users)"); + assert.ok(isSelect(ast), 'Should be Select'); + const where = ast.where; + assert.strictEqual(where.type, 'function'); + assert.strictEqual(where.name.name[0].value, 'EXISTS'); + }); +}); diff --git a/test/types/mysql/clause-types.spec.ts b/test/types/mysql/clause-types.spec.ts new file mode 100644 index 00000000..a4e5d99e --- /dev/null +++ b/test/types/mysql/clause-types.spec.ts @@ -0,0 +1,90 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, OrderBy, Limit, LimitValue } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('OrderBy - ASC', () => { + const sql = 'SELECT * FROM users ORDER BY name ASC'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const orderby = ast.orderby as OrderBy[]; + assert.ok(orderby); + assert.strictEqual(orderby[0].type, 'ASC'); + assert.strictEqual('expr' in orderby[0], true, 'expr should be present'); +}); + +test('OrderBy - DESC', () => { + const sql = 'SELECT * FROM users ORDER BY name DESC'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const orderby = ast.orderby as OrderBy[]; + assert.strictEqual(orderby[0].type, 'DESC'); +}); + +test('OrderBy - no direction (default)', () => { + const sql = 'SELECT * FROM users ORDER BY name'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const orderby = ast.orderby as OrderBy[]; + assert.strictEqual('type' in orderby[0], true, 'type should be present'); +}); + +test('Limit - single value', () => { + const sql = 'SELECT * FROM users LIMIT 10'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const limit = ast.limit as Limit; + assert.ok(limit); + assert.strictEqual('seperator' in limit, true, 'seperator should be present'); + assert.strictEqual('value' in limit, true, 'value should be present'); + assert.ok(Array.isArray(limit.value)); + assert.strictEqual(limit.value.length, 1); +}); + +test('Limit - offset and count', () => { + const sql = 'SELECT * FROM users LIMIT 10, 20'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const limit = ast.limit as Limit; + assert.strictEqual(limit.value.length, 2); + assert.strictEqual(limit.seperator, ','); +}); + +test('LimitValue - check properties', () => { + const sql = 'SELECT * FROM users LIMIT 10'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const limit = ast.limit as Limit; + const limitValue = limit.value[0] as LimitValue; + assert.strictEqual('type' in limitValue, true, 'type should be present'); + assert.strictEqual('value' in limitValue, true, 'value should be present'); +}); + +test('GroupBy - simple', () => { + const sql = 'SELECT COUNT(*) FROM users GROUP BY status'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + assert.ok(ast.groupby); + assert.strictEqual('columns' in ast.groupby, true, 'columns should be present'); + assert.strictEqual('modifiers' in ast.groupby, true, 'modifiers should be present'); + assert.ok(Array.isArray(ast.groupby.columns)); + assert.ok(Array.isArray(ast.groupby.modifiers)); +}); + +test('Having - with GROUP BY', () => { + const sql = 'SELECT COUNT(*) as cnt FROM users GROUP BY status HAVING cnt > 5'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + assert.ok(ast.having); +}); diff --git a/test/types/mysql/column-check-constraint.spec.ts b/test/types/mysql/column-check-constraint.spec.ts new file mode 100644 index 00000000..b5acf7c3 --- /dev/null +++ b/test/types/mysql/column-check-constraint.spec.ts @@ -0,0 +1,73 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateTable, CreateColumnDefinition } from '../../../types.d.ts'; +import { isCreateTable, isCreateColumnDefinition } from './types.guard.ts'; + +const parser = new Parser(); + +test('Column with CHECK constraint', () => { + const sql = 'CREATE TABLE t (age INT CHECK (age >= 0))'; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + + const colDef = create.create_definitions[0] as CreateColumnDefinition; + + // Verify check property exists + assert.ok(colDef.check); + + // Verify check structure + assert.strictEqual(colDef.check.constraint_type, 'check'); + assert.ok(Array.isArray(colDef.check.definition)); + assert.ok(colDef.check.definition.length > 0); +}); + +test('Column with named CHECK constraint', () => { + const sql = 'CREATE TABLE t (age INT CONSTRAINT age_check CHECK (age >= 0))'; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + + const colDef = create.create_definitions[0] as CreateColumnDefinition; + + // Verify check property with constraint name + assert.ok(colDef.check); + assert.strictEqual(colDef.check.constraint, 'age_check'); + assert.strictEqual(colDef.check.keyword, 'constraint'); +}); + +test('Column with CHECK constraint and ENFORCED', () => { + const sql = 'CREATE TABLE t (age INT CHECK (age >= 0) ENFORCED)'; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + + const colDef = create.create_definitions[0] as CreateColumnDefinition; + + // Verify check property with enforced + assert.ok(colDef.check); + assert.strictEqual(colDef.check.enforced, 'enforced'); +}); + +test('Column with CHECK constraint and NOT ENFORCED', () => { + const sql = 'CREATE TABLE t (age INT CHECK (age >= 0) NOT ENFORCED)'; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + + const colDef = create.create_definitions[0] as CreateColumnDefinition; + + // Verify check property with not enforced + assert.ok(colDef.check); + assert.strictEqual(colDef.check.enforced, 'not enforced'); +}); + diff --git a/test/types/mysql/column-check-generated.spec.ts b/test/types/mysql/column-check-generated.spec.ts new file mode 100644 index 00000000..210c16dc --- /dev/null +++ b/test/types/mysql/column-check-generated.spec.ts @@ -0,0 +1,59 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateTable, CreateColumnDefinition } from '../../../types.d.ts'; +import { isCreateTable } from './types.guard.ts'; + +const parser = new Parser(); + +test('Column with GENERATED (AS shorthand)', () => { + const sql = 'CREATE TABLE users (id INT, full_name VARCHAR(100) AS (CONCAT(id, id)))'; + const ast = parser.astify(sql); + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + const colDef = create.create_definitions[1] as CreateColumnDefinition; + assert.ok(colDef.generated); + assert.strictEqual(colDef.generated.type, 'generated'); + assert.ok(colDef.generated.expr); +}); + +test('Column with GENERATED STORED', () => { + const sql = 'CREATE TABLE users (id INT, full_name VARCHAR(100) AS (id) STORED)'; + const ast = parser.astify(sql); + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + const colDef = create.create_definitions[1] as CreateColumnDefinition; + assert.ok(colDef.generated); + assert.strictEqual(colDef.generated.type, 'generated'); + assert.strictEqual(colDef.generated.storage_type, 'stored'); +}); + +test('Column with GENERATED VIRTUAL', () => { + const sql = 'CREATE TABLE users (id INT, full_name VARCHAR(100) AS (id) VIRTUAL)'; + const ast = parser.astify(sql); + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + const colDef = create.create_definitions[1] as CreateColumnDefinition; + assert.ok(colDef.generated); + assert.strictEqual(colDef.generated.type, 'generated'); + assert.strictEqual(colDef.generated.storage_type, 'virtual'); +}); + +test('Column with unique variants', () => { + const sql1 = 'CREATE TABLE t1 (id INT UNIQUE)'; + const ast1 = parser.astify(sql1); + assert.ok(isCreateTable(ast1)); + const create1 = ast1 as CreateTable; + const colDef1 = create1.create_definitions![0] as CreateColumnDefinition; + assert.ok(colDef1.unique === 'unique' || colDef1.unique === 'unique key'); + + const sql2 = 'CREATE TABLE t2 (id INT UNIQUE KEY)'; + const ast2 = parser.astify(sql2); + assert.ok(isCreateTable(ast2)); + const create2 = ast2 as CreateTable; + const colDef2 = create2.create_definitions![0] as CreateColumnDefinition; + assert.ok(colDef2.unique === 'unique' || colDef2.unique === 'unique key'); +}); diff --git a/test/types/mysql/column-field-types.spec.ts b/test/types/mysql/column-field-types.spec.ts new file mode 100644 index 00000000..c8762e15 --- /dev/null +++ b/test/types/mysql/column-field-types.spec.ts @@ -0,0 +1,77 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Update, Insert_Replace, Column, SetList, InsertReplaceValue, Star } from '../../types.d.ts'; +import { isSelect, isUpdate, isInsert_Replace } from './types.guard.ts'; + +const parser = new Parser(); + +test('Column - simple', () => { + const sql = 'SELECT id FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const col = (ast.columns as Column[])[0]; + assert.strictEqual('expr' in col, true, 'expr should be present'); + assert.strictEqual('as' in col, true, 'as should be present'); +}); + +test('Column - with alias', () => { + const sql = 'SELECT id AS user_id FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const col = (ast.columns as Column[])[0]; + assert.strictEqual(col.as, 'user_id'); +}); + +test('SetList - UPDATE', () => { + const sql = 'UPDATE users SET name = "John" WHERE id = 1'; + const ast = parser.astify(sql); + + assert.ok(isUpdate(ast), 'AST should be an Update type'); + const setItem = ast.set[0] as SetList; + assert.strictEqual('column' in setItem, true, 'column should be present'); + assert.strictEqual('value' in setItem, true, 'value should be present'); + assert.strictEqual('table' in setItem, true, 'table should be present'); +}); + +test('SetList - with table prefix', () => { + const sql = 'UPDATE users SET users.name = "John" WHERE id = 1'; + const ast = parser.astify(sql); + + assert.ok(isUpdate(ast), 'AST should be an Update type'); + const setItem = ast.set[0] as SetList; + assert.ok(setItem.table); +}); + +test('InsertReplaceValue - INSERT VALUES', () => { + const sql = 'INSERT INTO users VALUES (1, "John")'; + const ast = parser.astify(sql); + + assert.ok(isInsert_Replace(ast), 'AST should be an Insert_Replace type'); + if (ast.values && typeof ast.values === 'object' && 'values' in ast.values) { + const valuesArray = (ast.values as any).values as InsertReplaceValue[]; + const value = valuesArray[0]; + assert.strictEqual('type' in value, true, 'type should be present'); + assert.strictEqual('value' in value, true, 'value should be present'); + assert.strictEqual('prefix' in value, true, 'prefix should be present'); + } +}); + +test('Star - SELECT *', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + if (ast.columns === '*') { + // It's just a string + } else if (Array.isArray(ast.columns)) { + // It's an array of columns + } else if (typeof ast.columns === 'object' && ast.columns !== null) { + const star = ast.columns as Star; + if ('type' in star && star.type === 'star') { + assert.strictEqual('value' in star, true, 'value should be present'); + } + } +}); diff --git a/test/types/mysql/column-unique-primary.spec.ts b/test/types/mysql/column-unique-primary.spec.ts new file mode 100644 index 00000000..431edead --- /dev/null +++ b/test/types/mysql/column-unique-primary.spec.ts @@ -0,0 +1,55 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateTable, CreateColumnDefinition } from '../../../types.d.ts'; +import { isCreateTable } from './types.guard.ts'; + +const parser = new Parser(); + +test('Column with UNIQUE', () => { + const sql = 'CREATE TABLE t (id INT UNIQUE)'; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + + const colDef = create.create_definitions[0] as CreateColumnDefinition; + assert.strictEqual(colDef.unique, 'unique'); +}); + +test('Column with UNIQUE KEY', () => { + const sql = 'CREATE TABLE t (id INT UNIQUE KEY)'; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + + const colDef = create.create_definitions[0] as CreateColumnDefinition; + assert.strictEqual(colDef.unique, 'unique key'); +}); + +test('Column with PRIMARY KEY', () => { + const sql = 'CREATE TABLE t (id INT PRIMARY KEY)'; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + + const colDef = create.create_definitions[0] as CreateColumnDefinition; + assert.strictEqual(colDef.primary_key, 'primary key'); +}); + +test('Column with KEY (shorthand for PRIMARY KEY)', () => { + const sql = 'CREATE TABLE t (id INT KEY)'; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.create_definitions); + + const colDef = create.create_definitions[0] as CreateColumnDefinition; + assert.strictEqual(colDef.primary_key, 'key'); +}); diff --git a/test/types/mysql/core-statements.spec.ts b/test/types/mysql/core-statements.spec.ts new file mode 100644 index 00000000..a57c0dfd --- /dev/null +++ b/test/types/mysql/core-statements.spec.ts @@ -0,0 +1,59 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Use, Explain, Transaction, Select } from '../../types.d.ts'; +import { isUse, isExplain, isTransaction, isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('USE statement', () => { + const sql = 'USE mydb'; + const ast = parser.astify(sql); + + assert.ok(isUse(ast), 'AST should be a Use type'); + const useAst = ast as Use; + assert.strictEqual(useAst.type, 'use'); + assert.strictEqual(useAst.db, 'mydb'); +}); + +test('EXPLAIN statement', () => { + const sql = 'EXPLAIN SELECT * FROM users'; + const ast = parser.astify(sql); + + assert.ok(isExplain(ast), 'AST should be an Explain type'); + const explainAst = ast as Explain; + assert.strictEqual(explainAst.type, 'explain'); + assert.ok(explainAst.expr); + assert.ok(isSelect(explainAst.expr), 'Expr should be a Select type'); +}); + +test('Transaction START', () => { + const sql = 'START TRANSACTION'; + const ast = parser.astify(sql); + + assert.ok(isTransaction(ast), 'AST should be a Transaction type'); + const txAst = ast as Transaction; + assert.strictEqual(txAst.type, 'transaction'); + assert.strictEqual(txAst.expr.action.value, 'start'); + assert.strictEqual(txAst.expr.keyword, 'TRANSACTION'); +}); + +test('Transaction COMMIT', () => { + const sql = 'COMMIT'; + const ast = parser.astify(sql); + + assert.ok(isTransaction(ast), 'AST should be a Transaction type'); + const txAst = ast as Transaction; + assert.strictEqual(txAst.type, 'transaction'); + assert.strictEqual(txAst.expr.action.value, 'COMMIT'); +}); + +test('Transaction ROLLBACK', () => { + const sql = 'ROLLBACK'; + const ast = parser.astify(sql); + + assert.ok(isTransaction(ast), 'AST should be a Transaction type'); + const txAst = ast as Transaction; + assert.strictEqual(txAst.type, 'transaction'); + assert.strictEqual(txAst.expr.action.value, 'ROLLBACK'); +}); diff --git a/test/types/mysql/create-constraints.spec.ts b/test/types/mysql/create-constraints.spec.ts new file mode 100644 index 00000000..62228753 --- /dev/null +++ b/test/types/mysql/create-constraints.spec.ts @@ -0,0 +1,64 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateConstraintPrimary, CreateConstraintUnique, CreateConstraintForeign, CreateConstraintCheck } from '../../types.d.ts'; +import { isCreate, isCreateConstraintPrimary, isCreateConstraintUnique, isCreateConstraintForeign, isCreateConstraintCheck } from './types.guard.ts'; + +const parser = new Parser(); + +test('CREATE TABLE with PRIMARY KEY constraint', () => { + const sql = 'CREATE TABLE users (id INT, PRIMARY KEY (id))'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const constraint = ast.create_definitions![1] as CreateConstraintPrimary; + + assert.ok(isCreateConstraintPrimary(constraint), 'Should be CreateConstraintPrimary'); + assert.strictEqual(constraint.constraint_type, 'primary key'); + assert.strictEqual(constraint.resource, 'constraint'); + assert.ok(Array.isArray(constraint.definition)); +}); + +test('CREATE TABLE with UNIQUE constraint', () => { + const sql = 'CREATE TABLE users (email VARCHAR(255), UNIQUE KEY (email))'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const constraint = ast.create_definitions![1] as CreateConstraintUnique; + + assert.ok(isCreateConstraintUnique(constraint), 'Should be CreateConstraintUnique'); + assert.strictEqual(constraint.constraint_type, 'unique key'); + assert.strictEqual(constraint.resource, 'constraint'); +}); + +test('CREATE TABLE with FOREIGN KEY constraint', () => { + const sql = 'CREATE TABLE orders (user_id INT, FOREIGN KEY (user_id) REFERENCES users(id))'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const constraint = ast.create_definitions![1] as CreateConstraintForeign; + + assert.ok(isCreateConstraintForeign(constraint), 'Should be CreateConstraintForeign'); + assert.strictEqual(constraint.resource, 'constraint'); + assert.ok(constraint.reference_definition); +}); + +test('CREATE TABLE with CHECK constraint', () => { + const sql = 'CREATE TABLE products (price INT, CHECK (price > 0))'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const constraint = ast.create_definitions![1] as CreateConstraintCheck; + + assert.ok(isCreateConstraintCheck(constraint), 'Should be CreateConstraintCheck'); + assert.strictEqual(constraint.constraint_type, 'check'); + assert.strictEqual(constraint.resource, 'constraint'); + assert.ok(Array.isArray(constraint.definition)); +}); + +test('CREATE TABLE with named constraint', () => { + const sql = 'CREATE TABLE users (id INT, CONSTRAINT pk_users PRIMARY KEY (id))'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const constraint = ast.create_definitions![1] as CreateConstraintPrimary; + + assert.ok(isCreateConstraintPrimary(constraint), 'Should be CreateConstraintPrimary'); + assert.strictEqual(constraint.constraint, 'pk_users'); + assert.strictEqual(constraint.keyword, 'constraint'); +}); diff --git a/test/types/mysql/create-definitions.spec.ts b/test/types/mysql/create-definitions.spec.ts new file mode 100644 index 00000000..f7aa26ca --- /dev/null +++ b/test/types/mysql/create-definitions.spec.ts @@ -0,0 +1,72 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateColumnDefinition, CreateIndexDefinition, CreateFulltextSpatialIndexDefinition } from '../../types.d.ts'; +import { isCreate, isCreateColumnDefinition, isCreateIndexDefinition, isCreateFulltextSpatialIndexDefinition } from './types.guard.ts'; + +const parser = new Parser(); + +test('CREATE TABLE with column definition - INT NOT NULL', () => { + const sql = 'CREATE TABLE users (id INT NOT NULL)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const colDef = ast.create_definitions![0] as CreateColumnDefinition; + + assert.ok(isCreateColumnDefinition(colDef), 'Should be CreateColumnDefinition'); + assert.strictEqual(colDef.resource, 'column'); + assert.ok(colDef.definition); + assert.strictEqual(colDef.definition.dataType, 'INT'); +}); + +test('CREATE TABLE with column definition - VARCHAR with DEFAULT', () => { + const sql = "CREATE TABLE users (name VARCHAR(255) DEFAULT 'unknown')"; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const colDef = ast.create_definitions![0] as CreateColumnDefinition; + + assert.ok(isCreateColumnDefinition(colDef), 'Should be CreateColumnDefinition'); + assert.ok(colDef.default_val); +}); + +test('CREATE TABLE with column definition - AUTO_INCREMENT', () => { + const sql = 'CREATE TABLE users (id INT AUTO_INCREMENT)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const colDef = ast.create_definitions![0] as CreateColumnDefinition; + + assert.ok(isCreateColumnDefinition(colDef), 'Should be CreateColumnDefinition'); + assert.strictEqual(colDef.auto_increment, 'auto_increment'); +}); + +test('CREATE TABLE with INDEX definition', () => { + const sql = 'CREATE TABLE users (id INT, name VARCHAR(255), INDEX idx_name (name))'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const indexDef = ast.create_definitions![2] as CreateIndexDefinition; + + assert.ok(isCreateIndexDefinition(indexDef), 'Should be CreateIndexDefinition'); + assert.strictEqual(indexDef.resource, 'index'); + assert.strictEqual(indexDef.keyword, 'index'); + assert.strictEqual(indexDef.index, 'idx_name'); +}); + +test('CREATE TABLE with KEY definition', () => { + const sql = 'CREATE TABLE users (id INT, KEY (id))'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const indexDef = ast.create_definitions![1] as CreateIndexDefinition; + + assert.ok(isCreateIndexDefinition(indexDef), 'Should be CreateIndexDefinition'); + assert.strictEqual(indexDef.keyword, 'key'); +}); + +test('CREATE TABLE with FULLTEXT INDEX', () => { + const sql = 'CREATE TABLE articles (id INT, content TEXT, FULLTEXT INDEX ft_content (content))'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + const ftIndex = ast.create_definitions![2] as CreateFulltextSpatialIndexDefinition; + + assert.ok(isCreateFulltextSpatialIndexDefinition(ftIndex), 'Should be CreateFulltextSpatialIndexDefinition'); + assert.strictEqual(ftIndex.resource, 'index'); + assert.strictEqual(ftIndex.keyword, 'fulltext index'); +}); diff --git a/test/types/mysql/create-extended.spec.ts b/test/types/mysql/create-extended.spec.ts new file mode 100644 index 00000000..cb88bcb1 --- /dev/null +++ b/test/types/mysql/create-extended.spec.ts @@ -0,0 +1,47 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Create, TriggerEvent, UserAuthOption } from '../../types.d.ts'; +import { isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('CREATE TRIGGER', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW SET NEW.created_at = NOW()'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.strictEqual(createAst.keyword, 'trigger'); + assert.ok(createAst.for_each); + assert.ok(Array.isArray(createAst.events)); + const event = createAst.events![0] as TriggerEvent; + assert.strictEqual(event.keyword, 'insert'); +}); + +test('CREATE VIEW', () => { + const sql = 'CREATE VIEW user_view AS SELECT id, name FROM users'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.strictEqual(createAst.keyword, 'view'); + assert.ok(createAst.view); + assert.ok(createAst.select); +}); + +test('CREATE USER - user is UserAuthOption array', () => { + const sql = "CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'password'"; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.strictEqual(createAst.keyword, 'user'); + assert.ok(Array.isArray(createAst.user)); + const user = createAst.user![0] as UserAuthOption; + assert.ok(user); + assert.ok(user.auth_option); +}); diff --git a/test/types/mysql/create-index-using.spec.ts b/test/types/mysql/create-index-using.spec.ts new file mode 100644 index 00000000..249d60fb --- /dev/null +++ b/test/types/mysql/create-index-using.spec.ts @@ -0,0 +1,40 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateIndex } from '../../../types.d.ts'; +import { isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('CREATE INDEX with USING BTREE after index name', () => { + const sql = 'CREATE INDEX idx USING BTREE ON t (col)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateIndex; + assert.strictEqual(create.type, 'create'); + assert.strictEqual(create.keyword, 'index'); + assert.ok(create.index_using); + assert.strictEqual(create.index_using.keyword, 'using'); + assert.strictEqual(create.index_using.type, 'btree'); +}); + +test('CREATE INDEX with USING HASH after index name', () => { + const sql = 'CREATE INDEX idx USING HASH ON t (col)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateIndex; + assert.ok(create.index_using); + assert.strictEqual(create.index_using.keyword, 'using'); + assert.strictEqual(create.index_using.type, 'hash'); +}); + +test('CREATE INDEX without USING', () => { + const sql = 'CREATE INDEX idx ON t (col)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateIndex; + assert.strictEqual(create.index_using, null); +}); diff --git a/test/types/mysql/create-table-like.spec.ts b/test/types/mysql/create-table-like.spec.ts new file mode 100644 index 00000000..c274b9f9 --- /dev/null +++ b/test/types/mysql/create-table-like.spec.ts @@ -0,0 +1,58 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateTable } from '../../../types.d.ts'; +import { isCreate, isCreateTable } from './types.guard.ts'; + +const parser = new Parser(); + +test('CREATE TABLE LIKE - basic', () => { + const sql = 'CREATE TABLE new_users LIKE users'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.strictEqual(create.type, 'create'); + assert.strictEqual(create.keyword, 'table'); + assert.ok(create.like); + assert.strictEqual(create.like.type, 'like'); + assert.ok(create.like.table); + assert.ok(Array.isArray(create.like.table)); +}); + +test('CREATE TABLE LIKE - with database prefix', () => { + const sql = 'CREATE TABLE new_db.new_users LIKE old_db.users'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.like); + assert.strictEqual(create.like.type, 'like'); + assert.ok(create.like.table); +}); + +test('CREATE TABLE LIKE - with parentheses', () => { + const sql = 'CREATE TABLE new_users (LIKE users)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.like); + assert.strictEqual(create.like.type, 'like'); + if (create.like.parentheses !== undefined) { + assert.strictEqual(create.like.parentheses, true); + } +}); + +test('CREATE TABLE without LIKE', () => { + const sql = 'CREATE TABLE users (id INT)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTable(ast)); + const create = ast as CreateTable; + assert.ok(create.like === null || create.like === undefined); +}); diff --git a/test/types/mysql/create-table-options.spec.ts b/test/types/mysql/create-table-options.spec.ts new file mode 100644 index 00000000..b0a4f4f5 --- /dev/null +++ b/test/types/mysql/create-table-options.spec.ts @@ -0,0 +1,20 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Create, TableOption } from '../../types.d.ts'; +import { isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('CREATE TABLE with table options', () => { + const sql = 'CREATE TABLE users (id INT) ENGINE=InnoDB DEFAULT CHARSET=utf8'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.ok(Array.isArray(createAst.table_options)); + const option = createAst.table_options![0] as TableOption; + assert.ok(option.keyword); + assert.ok(option.value); +}); diff --git a/test/types/mysql/create-trigger-table-property.spec.ts b/test/types/mysql/create-trigger-table-property.spec.ts new file mode 100644 index 00000000..f879bb6e --- /dev/null +++ b/test/types/mysql/create-trigger-table-property.spec.ts @@ -0,0 +1,41 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateTrigger } from '../../../types.d.ts'; +import { isCreate, isCreateTrigger } from './types.guard.ts'; + +const parser = new Parser(); + +test('CreateTrigger.table - single object form', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW SET x = 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.table, 'Should have table property'); + + // Check if it's a single object (not array) + if (!Array.isArray(ast.table)) { + assert.strictEqual(ast.table.db, null); + assert.strictEqual(ast.table.table, 'users'); + } else { + assert.fail('table should be a single object, not an array'); + } +}); + +test('CreateTrigger.table - with database name', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON mydb.users FOR EACH ROW SET x = 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.table, 'Should have table property'); + + // Check if it's a single object (not array) + if (!Array.isArray(ast.table)) { + assert.strictEqual(ast.table.db, 'mydb'); + assert.strictEqual(ast.table.table, 'users'); + } else { + assert.fail('table should be a single object, not an array'); + } +}); diff --git a/test/types/mysql/create-trigger.spec.ts b/test/types/mysql/create-trigger.spec.ts new file mode 100644 index 00000000..851f0f3c --- /dev/null +++ b/test/types/mysql/create-trigger.spec.ts @@ -0,0 +1,136 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateTrigger, TriggerEvent } from '../../../types.d.ts'; +import { isCreate, isCreateTrigger, isBinary } from './types.guard.ts'; + +const parser = new Parser(); + +test('CreateTrigger - basic BEFORE INSERT', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW SET NEW.created_at = NOW()'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.strictEqual(ast.type, 'create'); + assert.strictEqual(ast.keyword, 'trigger'); + assert.strictEqual(ast.time, 'BEFORE'); + assert.ok(Array.isArray(ast.events)); + assert.strictEqual(ast.events![0].keyword, 'insert'); +}); + +test('CreateTrigger - AFTER UPDATE', () => { + const sql = 'CREATE TRIGGER update_trigger AFTER UPDATE ON users FOR EACH ROW SET x = 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.strictEqual(ast.time, 'AFTER'); + assert.strictEqual(ast.events![0].keyword, 'update'); +}); + +test('CreateTrigger - AFTER DELETE', () => { + const sql = 'CREATE TRIGGER delete_trigger AFTER DELETE ON users FOR EACH ROW SET x = 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.strictEqual(ast.time, 'AFTER'); + assert.strictEqual(ast.events![0].keyword, 'delete'); +}); + +test('CreateTrigger - with definer', () => { + const sql = "CREATE DEFINER = 'admin'@'localhost' TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW SET NEW.created_at = NOW()"; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.definer, 'Should have definer'); + assert.ok(isBinary(ast.definer), 'Definer should be Binary'); +}); + +test('CreateTrigger - trigger with db.table name', () => { + const sql = 'CREATE TRIGGER mydb.my_trigger BEFORE INSERT ON mydb.users FOR EACH ROW SET NEW.created_at = NOW()'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.trigger); + assert.strictEqual(ast.trigger!.db, 'mydb'); + assert.strictEqual(ast.trigger!.table, 'my_trigger'); +}); + +test('CreateTrigger - table property', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW SET NEW.created_at = NOW()'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.table); +}); + +test('CreateTrigger - for_each property', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW SET NEW.created_at = NOW()'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.for_each); + assert.strictEqual(typeof ast.for_each, 'object'); + assert.strictEqual(ast.for_each.keyword, 'for each'); + assert.strictEqual(ast.for_each.args, 'row'); +}); + +test('CreateTrigger - for_each with STATEMENT', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH STATEMENT SET x = 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.for_each); + assert.strictEqual(ast.for_each.args, 'statement'); +}); + +test('CreateTrigger - execute property', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW SET NEW.created_at = NOW()'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.execute); + assert.strictEqual(ast.execute.type, 'set'); + assert.ok(Array.isArray(ast.execute.expr)); +}); + +test('CreateTrigger - with FOLLOWS order', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW FOLLOWS other_trigger SET x = 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.order); + assert.strictEqual(ast.order.keyword, 'FOLLOWS'); + assert.strictEqual(ast.order.trigger, 'other_trigger'); +}); + +test('CreateTrigger - with PRECEDES order', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW PRECEDES other_trigger SET x = 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + assert.ok(ast.order); + assert.strictEqual(ast.order.keyword, 'PRECEDES'); + assert.strictEqual(ast.order.trigger, 'other_trigger'); +}); + +test('CreateTrigger - TriggerEvent type', () => { + const sql = 'CREATE TRIGGER my_trigger BEFORE INSERT ON users FOR EACH ROW SET x = 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateTrigger(ast), 'Should be CreateTrigger'); + const event = ast.events![0] as TriggerEvent; + assert.strictEqual(event.keyword, 'insert'); + assert.strictEqual(event.args, undefined); +}); diff --git a/test/types/mysql/create-user.spec.ts b/test/types/mysql/create-user.spec.ts new file mode 100644 index 00000000..7cb4f586 --- /dev/null +++ b/test/types/mysql/create-user.spec.ts @@ -0,0 +1,64 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateUser, UserAuthOption } from '../../../types.d.ts'; +import { isCreate, isCreateUser, isValueExpr } from './types.guard.ts'; + +const parser = new Parser(); + +test('CreateUser - basic user creation', () => { + const sql = "CREATE USER 'testuser'@'localhost'"; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateUser(ast), 'Should be CreateUser'); + assert.strictEqual(ast.type, 'create'); + assert.strictEqual(ast.keyword, 'user'); + assert.ok(Array.isArray(ast.user)); +}); + +test('CreateUser - with password', () => { + const sql = "CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'password'"; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateUser(ast), 'Should be CreateUser'); + const user = ast.user![0] as UserAuthOption; + assert.ok(user.user); + assert.ok(user.auth_option); + assert.strictEqual(user.auth_option!.keyword, 'identified'); + assert.strictEqual(user.auth_option!.value.prefix, 'by'); +}); + +test('CreateUser - with IF NOT EXISTS', () => { + const sql = "CREATE USER IF NOT EXISTS 'testuser'@'localhost'"; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateUser(ast), 'Should be CreateUser'); + assert.strictEqual(ast.if_not_exists, 'IF NOT EXISTS'); +}); + +test('CreateUser - UserAuthOption user property', () => { + const sql = "CREATE USER 'testuser'@'localhost'"; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateUser(ast), 'Should be CreateUser'); + const user = ast.user![0] as UserAuthOption; + assert.ok(user.user); + assert.ok(user.user.name); + assert.strictEqual(user.user.name.type, 'single_quote_string'); + assert.ok(user.user.host); + assert.strictEqual(user.user.host.type, 'single_quote_string'); +}); + +test('CreateUser - multiple users', () => { + const sql = "CREATE USER 'user1'@'localhost', 'user2'@'%'"; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'Should be Create'); + assert.ok(isCreateUser(ast), 'Should be CreateUser'); + assert.ok(Array.isArray(ast.user)); + assert.strictEqual(ast.user!.length, 2); +}); diff --git a/test/types/mysql/create-variants.spec.ts b/test/types/mysql/create-variants.spec.ts new file mode 100644 index 00000000..7d8fcd46 --- /dev/null +++ b/test/types/mysql/create-variants.spec.ts @@ -0,0 +1,50 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Create } from '../../types.d.ts'; +import { isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('CREATE DATABASE', () => { + const sql = 'CREATE DATABASE mydb'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.strictEqual(createAst.keyword, 'database'); +}); + +test('CREATE SCHEMA', () => { + const sql = 'CREATE SCHEMA myschema'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.strictEqual(createAst.keyword, 'schema'); +}); + +test('CREATE INDEX', () => { + const sql = 'CREATE INDEX idx_name ON users (name)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.strictEqual(createAst.keyword, 'index'); + assert.strictEqual(createAst.index, 'idx_name'); + assert.ok(createAst.index_columns); +}); + +test('CREATE UNIQUE INDEX', () => { + const sql = 'CREATE UNIQUE INDEX idx_email ON users (email)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.strictEqual(createAst.keyword, 'index'); + assert.strictEqual(createAst.index_type, 'unique'); +}); diff --git a/test/types/mysql/create-view-properties.spec.ts b/test/types/mysql/create-view-properties.spec.ts new file mode 100644 index 00000000..3af58835 --- /dev/null +++ b/test/types/mysql/create-view-properties.spec.ts @@ -0,0 +1,169 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateView } from '../../../types.d.ts'; +import { isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('CREATE VIEW - view property with db and view name', () => { + const sql = 'CREATE VIEW mydb.myview AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.type, 'create'); + assert.strictEqual(create.keyword, 'view'); + assert.strictEqual(create.view.db, 'mydb'); + assert.strictEqual(create.view.view, 'myview'); +}); + +test('CREATE VIEW - view property with view name only', () => { + const sql = 'CREATE VIEW myview AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.view.db, null); + assert.strictEqual(create.view.view, 'myview'); +}); + +test('CREATE VIEW - replace property', () => { + const sql = 'CREATE OR REPLACE VIEW v AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.replace, 'or replace'); +}); + +test('CREATE VIEW - replace null', () => { + const sql = 'CREATE VIEW v AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.replace, null); +}); + +test('CREATE VIEW - algorithm UNDEFINED', () => { + const sql = 'CREATE ALGORITHM = UNDEFINED VIEW v AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.algorithm, 'UNDEFINED'); +}); + +test('CREATE VIEW - algorithm MERGE', () => { + const sql = 'CREATE ALGORITHM = MERGE VIEW v AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.algorithm, 'MERGE'); +}); + +test('CREATE VIEW - algorithm TEMPTABLE', () => { + const sql = 'CREATE ALGORITHM = TEMPTABLE VIEW v AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.algorithm, 'TEMPTABLE'); +}); + +test('CREATE VIEW - sql_security DEFINER', () => { + const sql = 'CREATE SQL SECURITY DEFINER VIEW v AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.sql_security, 'DEFINER'); +}); + +test('CREATE VIEW - sql_security INVOKER', () => { + const sql = 'CREATE SQL SECURITY INVOKER VIEW v AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.sql_security, 'INVOKER'); +}); + +test('CREATE VIEW - columns property as string array', () => { + const sql = 'CREATE VIEW myview (col1, col2) AS SELECT 1, 2'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.ok(Array.isArray(create.columns)); + assert.strictEqual(create.columns!.length, 2); + assert.strictEqual(create.columns![0], 'col1'); + assert.strictEqual(create.columns![1], 'col2'); +}); + +test('CREATE VIEW - columns property null', () => { + const sql = 'CREATE VIEW myview AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.columns, null); +}); + +test('CREATE VIEW - with CASCADED CHECK OPTION', () => { + const sql = 'CREATE VIEW myview AS SELECT 1 WITH CASCADED CHECK OPTION'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.with, 'with cascaded check option'); +}); + +test('CREATE VIEW - with LOCAL CHECK OPTION', () => { + const sql = 'CREATE VIEW myview AS SELECT 1 WITH LOCAL CHECK OPTION'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.with, 'with local check option'); +}); + +test('CREATE VIEW - with CHECK OPTION (no cascaded/local)', () => { + const sql = 'CREATE VIEW myview AS SELECT 1 WITH CHECK OPTION'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.with, 'with check option'); +}); + +test('CREATE VIEW - with property null', () => { + const sql = 'CREATE VIEW myview AS SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.with, null); +}); + +test('CREATE VIEW - all properties combined', () => { + const sql = "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = 'user'@'host' SQL SECURITY INVOKER VIEW mydb.myview (col1, col2) AS SELECT 1, 2 WITH LOCAL CHECK OPTION"; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast)); + const create = ast as CreateView; + assert.strictEqual(create.type, 'create'); + assert.strictEqual(create.keyword, 'view'); + assert.strictEqual(create.replace, 'or replace'); + assert.strictEqual(create.algorithm, 'MERGE'); + assert.ok(create.definer); + assert.strictEqual(create.sql_security, 'INVOKER'); + assert.strictEqual(create.view.db, 'mydb'); + assert.strictEqual(create.view.view, 'myview'); + assert.ok(create.columns); + assert.strictEqual(create.columns!.length, 2); + assert.ok(create.select); + assert.strictEqual(create.with, 'with local check option'); +}); diff --git a/test/types/mysql/data-definition-verify.spec.ts b/test/types/mysql/data-definition-verify.spec.ts new file mode 100644 index 00000000..61b23bb5 --- /dev/null +++ b/test/types/mysql/data-definition-verify.spec.ts @@ -0,0 +1,80 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateTable, CreateColumnDefinition, CreateIndexDefinition, DataType } from '../../types.d.ts'; +import { isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('DataType - simple INT', () => { + const sql = 'CREATE TABLE users (id INT)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const colDef = ast.create_definitions![0] as CreateColumnDefinition; + const dataType = colDef.definition as DataType; + assert.strictEqual('dataType' in dataType, true, 'dataType should be present'); +}); + +test('DataType - VARCHAR with length', () => { + const sql = 'CREATE TABLE users (name VARCHAR(255))'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const colDef = ast.create_definitions![0] as CreateColumnDefinition; + const dataType = colDef.definition as DataType; + assert.strictEqual(dataType.length, 255); +}); + +test('DataType - TEXT', () => { + const sql = 'CREATE TABLE articles (content TEXT)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const colDef = ast.create_definitions![0] as CreateColumnDefinition; + const dataType = colDef.definition as DataType; + assert.ok(dataType); +}); + +test('CreateColumnDefinition - simple', () => { + const sql = 'CREATE TABLE users (id INT)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const colDef = ast.create_definitions![0] as CreateColumnDefinition; + assert.strictEqual('column' in colDef, true, 'column should be present'); + assert.strictEqual('definition' in colDef, true, 'definition should be present'); + assert.strictEqual('resource' in colDef, true, 'resource should be present'); +}); + +test('CreateColumnDefinition - with NOT NULL', () => { + const sql = 'CREATE TABLE users (id INT NOT NULL)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const colDef = ast.create_definitions![0] as CreateColumnDefinition; + assert.ok(colDef.nullable); +}); + +test('CreateIndexDefinition - simple', () => { + const sql = 'CREATE TABLE users (id INT, INDEX idx_id (id))'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const indexDef = ast.create_definitions![1] as CreateIndexDefinition; + assert.strictEqual('resource' in indexDef, true, 'resource should be present'); + assert.strictEqual('keyword' in indexDef, true, 'keyword should be present'); + assert.strictEqual('definition' in indexDef, true, 'definition should be present'); + assert.strictEqual('index' in indexDef, true, 'index should be present'); + assert.strictEqual('index_type' in indexDef, true, 'index_type should be present'); + assert.strictEqual('index_options' in indexDef, true, 'index_options should be present'); +}); + +test('CreateIndexDefinition - without name', () => { + const sql = 'CREATE TABLE users (id INT, INDEX (id))'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const indexDef = ast.create_definitions![1] as CreateIndexDefinition; + assert.strictEqual('index' in indexDef, true, 'index should be present even without name'); +}); diff --git a/test/types/mysql/datatype-suffix-extended.spec.ts b/test/types/mysql/datatype-suffix-extended.spec.ts new file mode 100644 index 00000000..0a9248db --- /dev/null +++ b/test/types/mysql/datatype-suffix-extended.spec.ts @@ -0,0 +1,66 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateTable, CreateColumnDefinition } from '../../types.d.ts'; +import { isCreate, isCreateColumnDefinition } from './types.guard.ts'; + +const parser = new Parser(); + +test('DataType suffix - UNSIGNED', () => { + const sql = 'CREATE TABLE t (id INT UNSIGNED)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast)); + const createTable = ast as CreateTable; + const colDef = createTable.create_definitions?.[0] as CreateColumnDefinition; + assert.ok(isCreateColumnDefinition(colDef)); + const dataType = colDef.definition; + assert.ok(dataType.suffix); + assert.ok(Array.isArray(dataType.suffix)); + assert.ok(dataType.suffix.includes('UNSIGNED')); +}); + +test('DataType suffix - ZEROFILL', () => { + const sql = 'CREATE TABLE t (id INT ZEROFILL)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast)); + const createTable = ast as CreateTable; + const colDef = createTable.create_definitions?.[0] as CreateColumnDefinition; + assert.ok(isCreateColumnDefinition(colDef)); + const dataType = colDef.definition; + assert.ok(dataType.suffix); + assert.ok(Array.isArray(dataType.suffix)); + assert.ok(dataType.suffix.includes('ZEROFILL')); +}); + +test('DataType suffix - UNSIGNED ZEROFILL', () => { + const sql = 'CREATE TABLE t (id INT UNSIGNED ZEROFILL)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast)); + const createTable = ast as CreateTable; + const colDef = createTable.create_definitions?.[0] as CreateColumnDefinition; + assert.ok(isCreateColumnDefinition(colDef)); + const dataType = colDef.definition; + assert.ok(dataType.suffix); + assert.ok(Array.isArray(dataType.suffix)); + assert.ok(dataType.suffix.includes('UNSIGNED')); + assert.ok(dataType.suffix.includes('ZEROFILL')); +}); + +test('DataType suffix - Timezone NOT in MySQL', () => { + // Timezone (WITH/WITHOUT TIME ZONE) is PostgreSQL syntax, not MySQL + // This test documents that Timezone type in types.d.ts is not applicable to MySQL + const sql = 'CREATE TABLE t (created_at TIMESTAMP)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast)); + const createTable = ast as CreateTable; + const colDef = createTable.create_definitions?.[0] as CreateColumnDefinition; + assert.ok(isCreateColumnDefinition(colDef)); + const dataType = colDef.definition; + // suffix should not be a Timezone tuple for MySQL + if (dataType.suffix && Array.isArray(dataType.suffix)) { + // Should be UNSIGNED/ZEROFILL array, not Timezone tuple + assert.ok(dataType.suffix.every((s: string) => + s === 'UNSIGNED' || s === 'ZEROFILL' + )); + } +}); diff --git a/test/types/mysql/datatype-suffix.spec.ts b/test/types/mysql/datatype-suffix.spec.ts new file mode 100644 index 00000000..3edce1d1 --- /dev/null +++ b/test/types/mysql/datatype-suffix.spec.ts @@ -0,0 +1,69 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Create, CreateColumnDefinition } from '../../types.d.ts'; +import { isCreate, isCreateColumnDefinition } from './types.guard.js'; + +const parser = new Parser(); + +test('DataType with UNSIGNED suffix', () => { + const sql = 'CREATE TABLE users (age INT UNSIGNED)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create', 'Should be Create type'); + assert.ok(createAst.create_definitions, 'Should have create_definitions'); + + const colDef = createAst.create_definitions[0] as CreateColumnDefinition; + assert.ok(isCreateColumnDefinition(colDef), 'Should be CreateColumnDefinition'); + + const dataType = colDef.definition; + assert.strictEqual(dataType.dataType, 'INT', 'Should be INT type'); + assert.ok(Array.isArray(dataType.suffix), 'Suffix should be array'); + assert.ok(dataType.suffix.includes('UNSIGNED'), 'Should include UNSIGNED'); +}); + +test('DataType with UNSIGNED ZEROFILL suffix', () => { + const sql = 'CREATE TABLE users (age INT UNSIGNED ZEROFILL)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create', 'Should be Create type'); + assert.ok(createAst.create_definitions, 'Should have create_definitions'); + + const colDef = createAst.create_definitions[0] as CreateColumnDefinition; + assert.ok(isCreateColumnDefinition(colDef), 'Should be CreateColumnDefinition'); + + const dataType = colDef.definition; + assert.strictEqual(dataType.dataType, 'INT', 'Should be INT type'); + assert.ok(Array.isArray(dataType.suffix), 'Suffix should be array'); + assert.ok(dataType.suffix.includes('UNSIGNED'), 'Should include UNSIGNED'); + assert.ok(dataType.suffix.includes('ZEROFILL'), 'Should include ZEROFILL'); +}); + +test('DataType with ON UPDATE in reference_definition', () => { + const sql = 'CREATE TABLE users (updated_at TIMESTAMP ON UPDATE CURRENT_TIMESTAMP)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create', 'Should be Create type'); + assert.ok(createAst.create_definitions, 'Should have create_definitions'); + + const colDef = createAst.create_definitions[0] as CreateColumnDefinition; + assert.ok(isCreateColumnDefinition(colDef), 'Should be CreateColumnDefinition'); + + const dataType = colDef.definition; + assert.strictEqual(dataType.dataType, 'TIMESTAMP', 'Should be TIMESTAMP type'); + + // ON UPDATE CURRENT_TIMESTAMP is stored in reference_definition.on_action, not in DataType.suffix + if ('reference_definition' in colDef && colDef.reference_definition) { + const refDef = colDef.reference_definition as any; + assert.ok(refDef.on_action, 'Should have on_action'); + assert.ok(Array.isArray(refDef.on_action), 'on_action should be array'); + const onUpdate = refDef.on_action[0]; + assert.strictEqual(onUpdate.type, 'on update', 'Should be on update type'); + } +}); diff --git a/test/types/mysql/delete-addition.spec.ts b/test/types/mysql/delete-addition.spec.ts new file mode 100644 index 00000000..e662cb0b --- /dev/null +++ b/test/types/mysql/delete-addition.spec.ts @@ -0,0 +1,39 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Delete } from '../../../types.d.ts'; +import { isDelete } from './types.guard.ts'; + +const parser = new Parser(); + +test('Delete.table.addition property', async (t) => { + await t.test('should set addition: true for single-table DELETE without explicit table list', () => { + const sql = 'DELETE FROM users WHERE id = 1'; + const ast = parser.astify(sql); + + assert.ok(isDelete(ast), 'Expected Delete AST'); + assert.ok(ast.table && ast.table.length > 0, 'Expected table array'); + assert.strictEqual(ast.table[0].addition, true, 'Expected addition: true'); + }); + + await t.test('should not set addition for multi-table DELETE', () => { + const sql = 'DELETE t1, t2 FROM users t1 JOIN orders t2 ON t1.id = t2.user_id WHERE t1.id = 1'; + const ast = parser.astify(sql); + + assert.ok(isDelete(ast), 'Expected Delete AST'); + assert.ok(ast.table && ast.table.length > 0, 'Expected table array'); + + // Check that addition is not set (undefined or false) + const hasAddition = ast.table.some(t => t.addition === true); + assert.strictEqual(hasAddition, false, 'Expected no addition property for multi-table DELETE'); + }); + + await t.test('should handle DELETE with table alias', () => { + const sql = 'DELETE FROM users AS u WHERE u.id = 1'; + const ast = parser.astify(sql); + + assert.ok(isDelete(ast), 'Expected Delete AST'); + assert.ok(ast.table && ast.table.length > 0, 'Expected table array'); + assert.strictEqual(ast.table[0].addition, true, 'Expected addition: true for single-table DELETE with alias'); + }); +}); diff --git a/test/types/mysql/delete-properties.spec.ts b/test/types/mysql/delete-properties.spec.ts new file mode 100644 index 00000000..3283b57b --- /dev/null +++ b/test/types/mysql/delete-properties.spec.ts @@ -0,0 +1,169 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Delete, From, Dual, Binary, Unary, Function as FunctionType } from '../../../types.d.ts'; +import { isDelete, isFrom, isDual, isBinary, isUnary, isFunction } from './types.guard.ts'; + +const parser = new Parser(); + +// Test Delete.with - With[] variant +test('Delete.with as With[]', () => { + const sql = 'WITH cte AS (SELECT id FROM temp) DELETE FROM users WHERE id IN (SELECT id FROM cte)'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.ok(Array.isArray(del.with)); + assert.strictEqual(del.with!.length, 1); + assert.strictEqual(del.with![0].name.value, 'cte'); +}); + +// Test Delete.with - null variant +test('Delete.with as null', () => { + const sql = 'DELETE FROM users'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.strictEqual(del.with, null); +}); + +// Test Delete.table - with addition property +test('Delete.table with addition property', () => { + const sql = 'DELETE t1, t2 FROM t1 INNER JOIN t2 WHERE t1.id = t2.id'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + if (del.table) { + assert.ok(Array.isArray(del.table)); + // Check if addition property exists + assert.ok(del.table.length > 0); + } +}); + +// Test Delete.table - array variant (simple DELETE has addition:true) +test('Delete.table with addition:true (simple DELETE)', () => { + const sql = 'DELETE FROM users WHERE id = 1'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + // Simple DELETE FROM creates table array with addition:true + assert.ok(Array.isArray(del.table)); + assert.strictEqual(del.table!.length, 1); + assert.strictEqual(del.table![0].addition, true); + assert.strictEqual(del.table![0].table, 'users'); +}); + +// Test Delete.from - Array with From +test('Delete.from as Array', () => { + const sql = 'DELETE FROM users'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.ok(Array.isArray(del.from)); + assert.strictEqual(del.from.length, 1); + assert.ok(isFrom(del.from[0])); + const from = del.from[0] as From; + assert.strictEqual(from.table, 'users'); +}); + +// Test Delete.from - with multiple tables (JOIN) +test('Delete.from with multiple tables (JOIN)', () => { + const sql = 'DELETE FROM users u JOIN orders o ON u.id = o.user_id WHERE u.id = 1'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.ok(Array.isArray(del.from)); + assert.ok(del.from.length >= 1); +}); + +// Test Delete.where - Binary variant +test('Delete.where as Binary', () => { + const sql = 'DELETE FROM users WHERE id = 1'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.ok(del.where); + assert.ok(isBinary(del.where)); + const binary = del.where as Binary; + assert.strictEqual(binary.type, 'binary_expr'); +}); + +// Test Delete.where - Unary variant +test('Delete.where as Unary', () => { + const sql = 'DELETE FROM users WHERE NOT active'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.ok(del.where); + if (del.where && 'type' in del.where && del.where.type === 'unary_expr') { + assert.ok(isUnary(del.where)); + } +}); + +// Test Delete.where - Function variant +test('Delete.where as Function', () => { + const sql = 'DELETE FROM users WHERE ISNULL(deleted_at)'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.ok(del.where); + if (del.where && 'type' in del.where && del.where.type === 'function') { + assert.ok(isFunction(del.where)); + } +}); + +// Test Delete.where - null variant +test('Delete.where as null', () => { + const sql = 'DELETE FROM users'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.strictEqual(del.where, null); +}); + +// Test Delete.orderby - OrderBy[] variant +test('Delete.orderby as OrderBy[]', () => { + const sql = 'DELETE FROM users ORDER BY id DESC'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.ok(Array.isArray(del.orderby)); + assert.strictEqual(del.orderby!.length, 1); + assert.strictEqual(del.orderby![0].type, 'DESC'); +}); + +// Test Delete.orderby - null variant +test('Delete.orderby as null', () => { + const sql = 'DELETE FROM users'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.strictEqual(del.orderby, null); +}); + +// Test Delete.limit - Limit variant +test('Delete.limit as Limit', () => { + const sql = 'DELETE FROM users LIMIT 10'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.ok(del.limit); + assert.ok(Array.isArray(del.limit!.value)); +}); + +// Test Delete.limit - null variant +test('Delete.limit as null', () => { + const sql = 'DELETE FROM users'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.strictEqual(del.limit, null); +}); + +// Test Delete.type +test('Delete.type as "delete"', () => { + const sql = 'DELETE FROM users'; + const ast = parser.astify(sql); + assert.ok(isDelete(ast)); + const del = ast as Delete; + assert.strictEqual(del.type, 'delete'); +}); diff --git a/test/types/mysql/delete.spec.ts b/test/types/mysql/delete.spec.ts new file mode 100644 index 00000000..bed07d04 --- /dev/null +++ b/test/types/mysql/delete.spec.ts @@ -0,0 +1,33 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Delete, From, Binary } from '../../types.d.ts'; +import { isDelete, isBinary } from './types.guard.ts'; + +const parser = new Parser(); + +test('DELETE with WHERE', () => { + const sql = 'DELETE FROM users WHERE id = 1'; + const ast = parser.astify(sql); + + assert.ok(isDelete(ast), 'AST should be a Delete type'); + const deleteAst = ast as Delete; + assert.strictEqual(deleteAst.type, 'delete'); + assert.ok(Array.isArray(deleteAst.from)); + assert.strictEqual((deleteAst.from as From[])[0].table, 'users'); + assert.ok(deleteAst.where); + assert.ok(isBinary(deleteAst.where), 'WHERE should be a Binary expression'); + const where = deleteAst.where as Binary; + assert.strictEqual(where.type, 'binary_expr'); +}); + +test('DELETE without WHERE', () => { + const sql = 'DELETE FROM users'; + const ast = parser.astify(sql); + + assert.ok(isDelete(ast), 'AST should be a Delete type'); + const deleteAst = ast as Delete; + assert.strictEqual(deleteAst.type, 'delete'); + assert.ok(Array.isArray(deleteAst.from)); + assert.strictEqual(deleteAst.where, null); +}); diff --git a/test/types/mysql/desc.spec.ts b/test/types/mysql/desc.spec.ts new file mode 100644 index 00000000..c69fa921 --- /dev/null +++ b/test/types/mysql/desc.spec.ts @@ -0,0 +1,24 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import { isDesc } from './types.guard.ts'; + +const parser = new Parser(); + +describe('Desc Statement', () => { + test('DESCRIBE statement', () => { + const ast = parser.astify("DESCRIBE users"); + + assert.ok(isDesc(ast), 'Should be Desc type'); + assert.strictEqual(ast.type, 'desc'); + assert.strictEqual(ast.table, 'users'); + }); + + test('DESC statement (short form)', () => { + const ast = parser.astify("DESC users"); + + assert.ok(isDesc(ast), 'Should be Desc type'); + assert.strictEqual(ast.type, 'desc'); + assert.strictEqual(ast.table, 'users'); + }); +}); diff --git a/test/types/mysql/dml-extended.spec.ts b/test/types/mysql/dml-extended.spec.ts new file mode 100644 index 00000000..ca6c4024 --- /dev/null +++ b/test/types/mysql/dml-extended.spec.ts @@ -0,0 +1,42 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Insert_Replace, Delete, From } from '../../types.d.ts'; +import { isInsert_Replace, isDelete } from './types.guard.ts'; + +const parser = new Parser(); + +test('INSERT with ON DUPLICATE KEY UPDATE', () => { + const sql = 'INSERT INTO users (id, name) VALUES (1, "John") ON DUPLICATE KEY UPDATE name = "Jane"'; + const ast = parser.astify(sql); + + assert.ok(isInsert_Replace(ast), 'AST should be an Insert_Replace type'); + const insertAst = ast as Insert_Replace; + assert.strictEqual(insertAst.type, 'insert'); + assert.ok(insertAst.on_duplicate_update); + assert.strictEqual(insertAst.on_duplicate_update.keyword, 'on duplicate key update'); + assert.ok(Array.isArray(insertAst.on_duplicate_update.set)); +}); + +test('INSERT with SET', () => { + const sql = 'INSERT INTO users SET name = "John", email = "john@example.com"'; + const ast = parser.astify(sql); + + assert.ok(isInsert_Replace(ast), 'AST should be an Insert_Replace type'); + const insertAst = ast as Insert_Replace; + assert.strictEqual(insertAst.type, 'insert'); + assert.ok(insertAst.set); + assert.ok(Array.isArray(insertAst.set)); +}); + +test('DELETE with table addition flag', () => { + const sql = 'DELETE t1 FROM users t1 JOIN orders t2 ON t1.id = t2.user_id'; + const ast = parser.astify(sql); + + assert.ok(isDelete(ast), 'AST should be a Delete type'); + const deleteAst = ast as Delete; + assert.strictEqual(deleteAst.type, 'delete'); + assert.ok(Array.isArray(deleteAst.table)); + const table = deleteAst.table![0] as From & { addition?: boolean }; + assert.ok(table); +}); diff --git a/test/types/mysql/drop-trigger.spec.ts b/test/types/mysql/drop-trigger.spec.ts new file mode 100644 index 00000000..2702e8ff --- /dev/null +++ b/test/types/mysql/drop-trigger.spec.ts @@ -0,0 +1,35 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { DropTrigger } from '../../../types.d.ts'; +import { isDropTrigger } from './types.guard.ts'; + +const parser = new Parser(); + +test('DropTrigger - basic', () => { + const sql = 'DROP TRIGGER my_trigger'; + const ast = parser.astify(sql); + + assert.ok(isDropTrigger(ast), 'Should be DropTrigger'); + assert.strictEqual(ast.type, 'drop'); + assert.strictEqual(ast.keyword, 'trigger'); + assert.ok(Array.isArray(ast.name)); +}); + +test('DropTrigger - with IF EXISTS', () => { + const sql = 'DROP TRIGGER IF EXISTS my_trigger'; + const ast = parser.astify(sql); + + assert.ok(isDropTrigger(ast), 'Should be DropTrigger'); + assert.strictEqual(ast.prefix, 'if exists'); +}); + +test('DropTrigger - with schema', () => { + const sql = 'DROP TRIGGER mydb.my_trigger'; + const ast = parser.astify(sql); + + assert.ok(isDropTrigger(ast), 'Should be DropTrigger'); + assert.ok(Array.isArray(ast.name)); + assert.strictEqual(ast.name[0].schema, 'mydb'); + assert.strictEqual(ast.name[0].trigger, 'my_trigger'); +}); diff --git a/test/types/mysql/drop.spec.ts b/test/types/mysql/drop.spec.ts new file mode 100644 index 00000000..29568a75 --- /dev/null +++ b/test/types/mysql/drop.spec.ts @@ -0,0 +1,28 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Drop } from '../../types.d.ts'; +import { isDrop } from './types.guard.ts'; + +const parser = new Parser(); + +test('DROP TABLE', () => { + const sql = 'DROP TABLE users'; + const ast = parser.astify(sql); + + assert.ok(isDrop(ast), 'AST should be a Drop type'); + const dropAst = ast as Drop; + assert.strictEqual(dropAst.type, 'drop'); + assert.strictEqual(dropAst.keyword, 'table'); + assert.ok(Array.isArray(dropAst.name)); +}); + +test('DROP TABLE IF EXISTS', () => { + const sql = 'DROP TABLE IF EXISTS users'; + const ast = parser.astify(sql); + + assert.ok(isDrop(ast), 'AST should be a Drop type'); + const dropAst = ast as Drop; + assert.strictEqual(dropAst.type, 'drop'); + assert.strictEqual(dropAst.prefix, 'if exists'); +}); diff --git a/test/types/mysql/edge-cases-extended.spec.ts b/test/types/mysql/edge-cases-extended.spec.ts new file mode 100644 index 00000000..000673c4 --- /dev/null +++ b/test/types/mysql/edge-cases-extended.spec.ts @@ -0,0 +1,107 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Insert_Replace, Drop, Create } from '../../types.d.ts'; +import { isSelect, isInsert_Replace, isDrop, isCreate } from './types.guard.js'; + +const parser = new Parser(); + +test('LIMIT with OFFSET', () => { + const sql = 'SELECT * FROM users LIMIT 10 OFFSET 20'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'Should be Select'); + const select = ast as Select; + assert.ok(select.limit, 'Should have limit'); + assert.ok(Array.isArray(select.limit.value), 'Limit value should be array'); + assert.strictEqual(select.limit.value.length, 2, 'Should have 2 limit values'); + assert.strictEqual(select.limit.seperator, 'offset', 'Should have offset separator'); + assert.strictEqual(select.limit.value[0].value, 10, 'First value should be limit'); + assert.strictEqual(select.limit.value[1].value, 20, 'Second value should be offset'); +}); + +test('LIMIT with comma syntax', () => { + const sql = 'SELECT * FROM users LIMIT 20, 10'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'Should be Select'); + const select = ast as Select; + assert.ok(select.limit, 'Should have limit'); + assert.strictEqual(select.limit.seperator, ',', 'Should have comma separator'); + assert.ok(Array.isArray(select.limit.value), 'Limit value should be array'); + assert.strictEqual(select.limit.value.length, 2, 'Should have 2 limit values'); +}); + +test('CASE with multiple WHEN clauses', () => { + const sql = 'SELECT CASE WHEN age < 13 THEN "child" WHEN age < 18 THEN "teen" WHEN age < 65 THEN "adult" ELSE "senior" END FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'Should be Select'); + const select = ast as Select; + const col = select.columns[0]; + + if (typeof col.expr === 'object' && col.expr !== null && 'type' in col.expr && col.expr.type === 'case') { + assert.ok(Array.isArray(col.expr.args), 'Should have args array'); + assert.strictEqual(col.expr.args.length, 4, 'Should have 4 args (3 WHEN + 1 ELSE)'); + + const whenClauses = col.expr.args.filter(arg => arg.type === 'when'); + assert.strictEqual(whenClauses.length, 3, 'Should have 3 WHEN clauses'); + + const elseClause = col.expr.args.find(arg => arg.type === 'else'); + assert.ok(elseClause, 'Should have ELSE clause'); + } +}); + +test('DROP with IF EXISTS', () => { + const sql = 'DROP TABLE IF EXISTS users'; + const ast = parser.astify(sql); + + assert.ok(isDrop(ast), 'Should be Drop'); + const drop = ast as Drop; + assert.strictEqual(drop.prefix, 'if exists', 'Should have if exists prefix'); +}); + +test('INSERT with IGNORE prefix', () => { + const sql = 'INSERT IGNORE INTO users (name) VALUES ("John")'; + const ast = parser.astify(sql); + + assert.ok(isInsert_Replace(ast), 'Should be Insert_Replace'); + const insert = ast as Insert_Replace; + assert.strictEqual(insert.prefix, 'ignore into', 'Should have ignore into prefix'); +}); + +test('DataType with length and scale', () => { + const sql = 'CREATE TABLE products (price DECIMAL(10, 2))'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create', 'Should be Create type'); + assert.ok(createAst.create_definitions, 'Should have create_definitions'); + + const colDef = createAst.create_definitions[0]; + if ('definition' in colDef) { + const dataType = colDef.definition; + assert.strictEqual(dataType.dataType, 'DECIMAL', 'Should be DECIMAL type'); + assert.strictEqual(dataType.length, 10, 'Should have length 10'); + assert.strictEqual(dataType.scale, 2, 'Should have scale 2'); + } +}); + +test('DataType with parentheses flag', () => { + const sql = 'CREATE TABLE users (name VARCHAR(50))'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create', 'Should be Create type'); + assert.ok(createAst.create_definitions, 'Should have create_definitions'); + + const colDef = createAst.create_definitions[0]; + if ('definition' in colDef) { + const dataType = colDef.definition; + assert.strictEqual(dataType.dataType, 'VARCHAR', 'Should be VARCHAR type'); + assert.strictEqual(dataType.length, 50, 'Should have length 50'); + assert.strictEqual(dataType.parentheses, true, 'Should have parentheses flag'); + } +}); diff --git a/test/types/mysql/edge-cases.spec.ts b/test/types/mysql/edge-cases.spec.ts new file mode 100644 index 00000000..7d4e9a45 --- /dev/null +++ b/test/types/mysql/edge-cases.spec.ts @@ -0,0 +1,105 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Insert_Replace, Alter, Lock, Create } from '../../types.d.ts'; +import { isSelect, isBinary, isInsert_Replace, isAlter, isLock, isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('Select with INTO has full structure', () => { + const sql = 'SELECT id INTO @var FROM t'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.ok(selectAst.into); + assert.strictEqual(selectAst.into.keyword, 'var'); + assert.strictEqual(selectAst.into.type, 'into'); + assert.ok(Array.isArray(selectAst.into.expr)); + assert.strictEqual(selectAst.into.position, 'column'); +}); + +test('Select with HAVING is Binary not array', () => { + const sql = 'SELECT COUNT(*) FROM t GROUP BY id HAVING COUNT(*) > 1'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.ok(selectAst.having); + assert.ok(isBinary(selectAst.having)); + assert.strictEqual(selectAst.having.operator, '>'); +}); + +test('Insert with PARTITION is string array', () => { + const sql = 'INSERT INTO t PARTITION (p0) VALUES (1)'; + const ast = parser.astify(sql); + + assert.ok(isInsert_Replace(ast), 'AST should be an Insert_Replace type'); + const insertAst = ast as Insert_Replace; + assert.strictEqual(insertAst.type, 'insert'); + assert.ok(Array.isArray(insertAst.partition)); + assert.strictEqual(insertAst.partition[0], 'p0'); +}); + +test('Alter expr is an array', () => { + const sql = 'ALTER TABLE t ADD COLUMN c INT'; + const ast = parser.astify(sql); + + assert.ok(isAlter(ast), 'AST should be an Alter type'); + const alterAst = ast as Alter; + assert.strictEqual(alterAst.type, 'alter'); + assert.ok(Array.isArray(alterAst.expr)); + assert.strictEqual(alterAst.expr[0].action, 'add'); + assert.strictEqual(alterAst.expr[0].keyword, 'COLUMN'); +}); + +test('Lock tables has object lock_type', () => { + const sql = 'LOCK TABLES t1 READ, t2 WRITE'; + const ast = parser.astify(sql); + + assert.ok(isLock(ast), 'AST should be a Lock type'); + const lockAst = ast as Lock; + assert.strictEqual(lockAst.type, 'lock'); + assert.ok(Array.isArray(lockAst.tables)); + assert.strictEqual(lockAst.tables.length, 2); + assert.strictEqual(typeof lockAst.tables[0].lock_type, 'object'); + assert.strictEqual(lockAst.tables[0].lock_type.type, 'read'); + assert.strictEqual(lockAst.tables[1].lock_type.type, 'write'); +}); + +test('Create table LIKE has From array', () => { + const sql = 'CREATE TABLE t2 LIKE t1'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.ok(createAst.like); + assert.strictEqual(createAst.like.type, 'like'); + assert.ok(Array.isArray(createAst.like.table)); + assert.strictEqual(createAst.like.table[0].table, 't1'); +}); + +test('Create view with DEFINER is Binary', () => { + const sql = "CREATE DEFINER = 'user'@'host' VIEW v AS SELECT 1"; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert.strictEqual(createAst.type, 'create'); + assert.ok(createAst.definer); + assert.ok(isBinary(createAst.definer)); + assert.strictEqual(createAst.definer.operator, '='); +}); + +test('Select with GROUP BY modifiers', () => { + const sql = 'SELECT COUNT(*) FROM t GROUP BY id WITH ROLLUP'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.ok(selectAst.groupby); + assert.ok(Array.isArray(selectAst.groupby.modifiers)); + assert.strictEqual(selectAst.groupby.modifiers[0].type, 'origin'); + assert.strictEqual(selectAst.groupby.modifiers[0].value, 'with rollup'); +}); diff --git a/test/types/mysql/expression-types.spec.ts b/test/types/mysql/expression-types.spec.ts new file mode 100644 index 00000000..4abb60ae --- /dev/null +++ b/test/types/mysql/expression-types.spec.ts @@ -0,0 +1,81 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Interval, Param, Value, ColumnRef, Function as FuncType } from '../../types.d.ts'; +import { isInterval, isParam, isValue, isSelect, isFunction, isColumnRef } from './types.guard.ts'; + +const parser = new Parser(); + +test('Interval expression', () => { + const sql = "SELECT DATE_ADD('2020-01-01', INTERVAL 1 DAY)"; + const ast = parser.astify(sql); + assert(isSelect(ast)); + const col = ast.columns[0]; + const func = col.expr as any; + const intervalArg = func.args.value[1]; + assert(isInterval(intervalArg)); + assert.strictEqual(intervalArg.type, 'interval'); + assert.strictEqual(intervalArg.unit, 'day'); +}); + +test('Param expression', () => { + const sql = "SELECT * FROM t WHERE id = :id"; + const ast = parser.astify(sql); + assert(isSelect(ast)); + const where = ast.where as any; + assert(isParam(where.right)); + assert.strictEqual(where.right.type, 'param'); + assert.strictEqual(where.right.value, 'id'); +}); + +test('Column ref with star', () => { + const sql = "SELECT * FROM t"; + const ast = parser.astify(sql); + assert(isSelect(ast)); + const col = ast.columns[0]; + assert(isColumnRef(col.expr)); + const colRef = col.expr as ColumnRef; + if ('column' in colRef) { + assert.strictEqual(colRef.column, '*'); + } +}); + +test('Value type - string', () => { + const sql = "SELECT 'hello'"; + const ast = parser.astify(sql); + assert(isSelect(ast)); + const col = ast.columns[0]; + assert(isValue(col.expr)); + assert.strictEqual(col.expr.type, 'single_quote_string'); + assert.strictEqual(col.expr.value, 'hello'); +}); + +test('Value type - number', () => { + const sql = "SELECT 42"; + const ast = parser.astify(sql); + assert(isSelect(ast)); + const col = ast.columns[0]; + assert(isValue(col.expr)); + assert.strictEqual(col.expr.type, 'number'); + assert.strictEqual(col.expr.value, 42); +}); + +test('Value type - boolean', () => { + const sql = "SELECT TRUE"; + const ast = parser.astify(sql); + assert(isSelect(ast)); + const col = ast.columns[0]; + assert(isValue(col.expr)); + assert.strictEqual(col.expr.type, 'bool'); + assert.strictEqual(col.expr.value, true); +}); + +test('Value type - null', () => { + const sql = "SELECT NULL"; + const ast = parser.astify(sql); + assert(isSelect(ast)); + const col = ast.columns[0]; + assert(isValue(col.expr)); + assert.strictEqual(col.expr.type, 'null'); + assert.strictEqual(col.expr.value, null); +}); diff --git a/test/types/mysql/expressions.spec.ts b/test/types/mysql/expressions.spec.ts new file mode 100644 index 00000000..2fd6f239 --- /dev/null +++ b/test/types/mysql/expressions.spec.ts @@ -0,0 +1,58 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Column, Function, AggrFunc, Case, Cast } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('SELECT with function', () => { + const sql = 'SELECT UPPER(name) FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + const cols = selectAst.columns as Column[]; + const func = cols[0].expr as Function; + assert.strictEqual(func.type, 'function'); +}); + +test('SELECT with aggregate function', () => { + const sql = 'SELECT COUNT(*) FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + const cols = selectAst.columns as Column[]; + const aggr = cols[0].expr as AggrFunc; + assert.strictEqual(aggr.type, 'aggr_func'); + assert.strictEqual(aggr.name, 'COUNT'); +}); + +test('SELECT with CASE expression', () => { + const sql = 'SELECT CASE WHEN age > 18 THEN "adult" ELSE "minor" END FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + const cols = selectAst.columns as Column[]; + const caseExpr = cols[0].expr as Case; + assert.strictEqual(caseExpr.type, 'case'); + assert.ok(Array.isArray(caseExpr.args)); +}); + +test('SELECT with CAST', () => { + const sql = 'SELECT CAST(id AS VARCHAR) FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + const cols = selectAst.columns as Column[]; + const cast = cols[0].expr as Cast; + assert.strictEqual(cast.type, 'cast'); + assert.strictEqual(cast.keyword, 'cast'); +}); diff --git a/test/types/mysql/from-as-property.spec.ts b/test/types/mysql/from-as-property.spec.ts new file mode 100644 index 00000000..25bf8165 --- /dev/null +++ b/test/types/mysql/from-as-property.spec.ts @@ -0,0 +1,40 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, BaseFrom } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('FROM without alias has as property set to null', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const from = selectAst.from as BaseFrom[]; + assert.strictEqual(from[0].as, null); + // Verify the property exists (not undefined) + assert.ok('as' in from[0]); +}); + +test('FROM with alias has as property set to string', () => { + const sql = 'SELECT * FROM users AS u'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const from = selectAst.from as BaseFrom[]; + assert.strictEqual(from[0].as, 'u'); +}); + +test('JOIN without alias has as property', () => { + const sql = 'SELECT * FROM users JOIN orders ON users.id = orders.user_id'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const from = selectAst.from as BaseFrom[]; + assert.ok('as' in from[0]); + assert.ok('as' in from[1]); +}); diff --git a/test/types/mysql/from-dual.spec.ts b/test/types/mysql/from-dual.spec.ts new file mode 100644 index 00000000..3cdac099 --- /dev/null +++ b/test/types/mysql/from-dual.spec.ts @@ -0,0 +1,45 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Dual } from '../../../types.d.ts'; +import { isSelect, isDual, isUpdate, isDelete } from './types.guard.ts'; + +const parser = new Parser(); + +test('FROM DUAL - basic usage', () => { + const sql = 'SELECT 1 FROM DUAL'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.from); + assert.ok(Array.isArray(select.from)); + const from = select.from[0]; + assert.ok(isDual(from)); + const dual = from as Dual; + assert.strictEqual(dual.type, 'dual'); +}); + +test('FROM DUAL - with expression', () => { + const sql = 'SELECT NOW() FROM DUAL'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.from); + assert.ok(Array.isArray(select.from)); + const from = select.from[0]; + assert.ok(isDual(from)); +}); + +test('UPDATE with DUAL', () => { + const sql = 'UPDATE t1, DUAL SET t1.col = 1'; + const ast = parser.astify(sql); + // This tests if Dual can appear in UPDATE table list + assert.ok(isUpdate(ast)); +}); + +test('DELETE with DUAL', () => { + const sql = 'DELETE t1 FROM t1, DUAL WHERE t1.id = 1'; + const ast = parser.astify(sql); + // This tests if Dual can appear in DELETE from list + assert.ok(isDelete(ast)); +}); diff --git a/test/types/mysql/from-types.spec.ts b/test/types/mysql/from-types.spec.ts new file mode 100644 index 00000000..c3ff32d5 --- /dev/null +++ b/test/types/mysql/from-types.spec.ts @@ -0,0 +1,107 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, BaseFrom, Join, TableExpr, Dual, From } from '../../types.d.ts'; +import { isSelect, isDelete } from './types.guard.ts'; + +const parser = new Parser(); + +test('BaseFrom - simple table', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const from = (ast.from as From[])[0] as BaseFrom; + assert.strictEqual(from.table, 'users'); + assert.strictEqual('db' in from, true, 'db should be present'); + assert.strictEqual('as' in from, true, 'as should be present'); +}); + +test('BaseFrom - with alias', () => { + const sql = 'SELECT * FROM users AS u'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const from = (ast.from as From[])[0] as BaseFrom; + assert.strictEqual(from.as, 'u'); +}); + +test('BaseFrom - with database', () => { + const sql = 'SELECT * FROM mydb.users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const from = (ast.from as From[])[0] as BaseFrom; + assert.strictEqual(from.db, 'mydb'); + assert.strictEqual(from.table, 'users'); +}); + +test('Join - INNER JOIN with ON', () => { + const sql = 'SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const join = (ast.from as From[])[1] as Join; + assert.strictEqual(join.join, 'INNER JOIN'); + assert.ok(join.on); + assert.strictEqual('using' in join, false, 'using should not be present when ON is used'); +}); + +test('Join - with USING', () => { + const sql = 'SELECT * FROM users INNER JOIN orders USING (user_id)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const join = (ast.from as From[])[1] as Join; + assert.ok(join.using); + assert.ok(Array.isArray(join.using)); +}); + +test('TableExpr - subquery', () => { + const sql = 'SELECT * FROM (SELECT id FROM users) AS sub'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const from = (ast.from as From[])[0] as TableExpr; + assert.ok(from.expr); + assert.ok(from.expr.ast); + assert.strictEqual('as' in from, true, 'as should be present'); + assert.strictEqual(from.as, 'sub'); +}); + +test('TableExpr - subquery without alias', () => { + const sql = 'SELECT * FROM (SELECT id FROM users)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + if (ast.from) { + const from = (ast.from as From[])[0] as TableExpr; + assert.strictEqual('as' in from, true, 'as should be present'); + } +}); + +test('Dual - SELECT without FROM', () => { + const sql = 'SELECT 1'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + if (ast.from) { + const from = (ast.from as From[])[0] as Dual; + if (from && 'type' in from) { + assert.strictEqual(from.type, 'dual'); + assert.strictEqual('loc' in from, true, 'loc should be present or absent'); + } + } +}); + +test('BaseFrom - with addition in DELETE', () => { + const sql = 'DELETE FROM users WHERE id = 1'; + const ast = parser.astify(sql); + + assert.ok(isDelete(ast), 'AST should be a Delete type'); + + const sql2 = 'DELETE users FROM users JOIN orders ON users.id = orders.user_id WHERE orders.status = "cancelled"'; + const ast2 = parser.astify(sql2); + + assert.ok(isDelete(ast2), 'AST should be a Delete type'); +}); diff --git a/test/types/mysql/function-name-schema.spec.ts b/test/types/mysql/function-name-schema.spec.ts new file mode 100644 index 00000000..19255fd3 --- /dev/null +++ b/test/types/mysql/function-name-schema.spec.ts @@ -0,0 +1,41 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Function, FunctionName } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('FunctionName - simple function name', () => { + const sql = 'SELECT UPPER(name) FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const func = col.expr as Function; + assert.strictEqual(func.type, 'function'); + assert.ok(func.name); + assert.ok(Array.isArray(func.name.name)); +}); + +test('FunctionName - schema-qualified function (if supported)', () => { + const sql = 'SELECT mydb.UPPER(name) FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const func = col.expr as Function; + if (func.type === 'function') { + const funcName = func.name as FunctionName; + // Check if schema property exists + assert.ok('schema' in funcName); + } +}); + +test('FunctionName - multi-part name', () => { + const sql = 'SELECT JSON_EXTRACT(data, "$.name") FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const func = col.expr as Function; + assert.strictEqual(func.type, 'function'); + assert.ok(func.name); +}); diff --git a/test/types/mysql/function-suffix.spec.ts b/test/types/mysql/function-suffix.spec.ts new file mode 100644 index 00000000..3ff07211 --- /dev/null +++ b/test/types/mysql/function-suffix.spec.ts @@ -0,0 +1,26 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Function, OnUpdateCurrentTimestamp } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('Function suffix type is OnUpdateCurrentTimestamp or null', () => { + const sql = 'SELECT CURRENT_TIMESTAMP() FROM dual'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const func = selectAst.columns[0].expr as Function; + assert.strictEqual(func.type, 'function'); + // suffix can be OnUpdateCurrentTimestamp | null | undefined + assert.ok(func.suffix === null || func.suffix === undefined || typeof func.suffix === 'object'); +}); + +test('CURRENT_TIMESTAMP without function ()', () => { + const sql = 'SELECT CURRENT_TIMESTAMP'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); +}); diff --git a/test/types/mysql/grant-loaddata-extended.spec.ts b/test/types/mysql/grant-loaddata-extended.spec.ts new file mode 100644 index 00000000..50e2043f --- /dev/null +++ b/test/types/mysql/grant-loaddata-extended.spec.ts @@ -0,0 +1,103 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Grant, LoadData } from '../../types.d.ts'; +import { isGrant, isLoadData } from './types.guard.ts'; + +const parser = new Parser(); + +test('GRANT statement', () => { + const sql = 'GRANT SELECT ON mydb.* TO user1'; + const ast = parser.astify(sql); + + assert.ok(isGrant(ast), 'AST should be a Grant type'); + const grantAst = ast as Grant; + assert.strictEqual(grantAst.type, 'grant'); +}); + +test('GRANT - objects with priv and columns', () => { + const sql = 'GRANT SELECT, INSERT ON mydb.* TO user1'; + const ast = parser.astify(sql); + assert.ok(isGrant(ast)); + const grantAst = ast as Grant; + assert.strictEqual(grantAst.objects.length, 2); + assert.strictEqual(grantAst.objects[0].priv.value, 'SELECT'); + assert.strictEqual(grantAst.objects[0].columns, null); +}); + +test('GRANT - on with object_type and priv_level', () => { + const sql = 'GRANT SELECT ON mydb.* TO user1'; + const ast = parser.astify(sql); + assert.ok(isGrant(ast)); + const grantAst = ast as Grant; + assert.strictEqual(grantAst.on.object_type, null); + assert.strictEqual(grantAst.on.priv_level[0].prefix, 'mydb'); + assert.strictEqual(grantAst.on.priv_level[0].name, '*'); +}); + +test('GRANT - user_or_roles with name and host', () => { + const sql = 'GRANT SELECT ON mydb.* TO user1@localhost'; + const ast = parser.astify(sql); + assert.ok(isGrant(ast)); + const grantAst = ast as Grant; + assert.strictEqual(grantAst.user_or_roles[0].name.value, 'user1'); + assert.ok(grantAst.user_or_roles[0].host); + assert.strictEqual(grantAst.user_or_roles[0].host.value, 'localhost'); +}); + +test('GRANT - to_from property', () => { + const sql = 'GRANT SELECT ON mydb.* TO user1'; + const ast = parser.astify(sql); + assert.ok(isGrant(ast)); + const grantAst = ast as Grant; + assert.strictEqual(grantAst.to_from, 'TO'); +}); + +test('LOAD DATA statement', () => { + const sql = "LOAD DATA INFILE '/tmp/data.csv' INTO TABLE users"; + const ast = parser.astify(sql); + + assert.ok(isLoadData(ast), 'AST should be a LoadData type'); + const loadAst = ast as LoadData; + assert.strictEqual(loadAst.type, 'load_data'); +}); + +test('LOAD DATA - mode, local, file properties', () => { + const sql = "LOAD DATA INFILE '/tmp/data.csv' INTO TABLE users"; + const ast = parser.astify(sql); + assert.ok(isLoadData(ast)); + const loadAst = ast as LoadData; + assert.strictEqual(loadAst.mode, null); + assert.strictEqual(loadAst.local, null); + assert.strictEqual(loadAst.file.value, '/tmp/data.csv'); +}); + +test('LOAD DATA - table property', () => { + const sql = "LOAD DATA INFILE '/tmp/data.csv' INTO TABLE mydb.users"; + const ast = parser.astify(sql); + assert.ok(isLoadData(ast)); + const loadAst = ast as LoadData; + assert.strictEqual(loadAst.table.db, 'mydb'); + assert.strictEqual(loadAst.table.table, 'users'); +}); + +test('LOAD DATA - fields property', () => { + const sql = "LOAD DATA INFILE '/tmp/data.csv' INTO TABLE users FIELDS TERMINATED BY ','"; + const ast = parser.astify(sql); + assert.ok(isLoadData(ast)); + const loadAst = ast as LoadData; + assert.ok(loadAst.fields); + assert.strictEqual(loadAst.fields.keyword, 'FIELDS'); + assert.ok(loadAst.fields.terminated); + assert.strictEqual(loadAst.fields.terminated.value, ','); +}); + +test('LOAD DATA - lines property', () => { + const sql = "LOAD DATA INFILE '/tmp/data.csv' INTO TABLE users LINES TERMINATED BY '\\n'"; + const ast = parser.astify(sql); + assert.ok(isLoadData(ast)); + const loadAst = ast as LoadData; + assert.ok(loadAst.lines); + assert.strictEqual(loadAst.lines.keyword, 'LINES'); + assert.ok(loadAst.lines.terminated); +}); diff --git a/test/types/mysql/helper-types.spec.ts b/test/types/mysql/helper-types.spec.ts new file mode 100644 index 00000000..f7747bfa --- /dev/null +++ b/test/types/mysql/helper-types.spec.ts @@ -0,0 +1,61 @@ +import { describe, test } from 'node:test'; +import { strict as assert } from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import { + isSelect, + isCreate, + isWindowExpr, + isNamedWindowExpr, + isTriggerEvent, + isTableOption, +} from './types.guard.js'; +import type { Select, Create } from '../../types.js'; + +const parser = new Parser(); + +describe('Helper Types', () => { + test('WindowExpr in SELECT with WINDOW clause', () => { + const sql = `SELECT id, ROW_NUMBER() OVER w FROM t WINDOW w AS (ORDER BY id)`; + const ast = parser.astify(sql); + + assert(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert(typeof selectAst === 'object' && selectAst !== null); + assert(selectAst.type === 'select'); + assert(selectAst.window !== undefined && selectAst.window !== null); + assert(isWindowExpr(selectAst.window)); + assert(selectAst.window.keyword === 'window'); + assert(Array.isArray(selectAst.window.expr)); + assert(selectAst.window.expr.length > 0); + assert(isNamedWindowExpr(selectAst.window.expr[0])); + }); + + test('TriggerEvent in CREATE TRIGGER', () => { + const sql = `CREATE TRIGGER my_trigger BEFORE INSERT ON t FOR EACH ROW SET x = 1`; + const ast = parser.astify(sql); + + assert(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert(typeof createAst === 'object' && createAst !== null); + assert(createAst.type === 'create'); + assert(createAst.events !== undefined && createAst.events !== null); + assert(Array.isArray(createAst.events)); + assert(createAst.events.length > 0); + assert(isTriggerEvent(createAst.events[0])); + assert(createAst.events[0].keyword === 'insert'); + }); + + test('TableOption in CREATE TABLE', () => { + const sql = `CREATE TABLE t (id INT) ENGINE=InnoDB AUTO_INCREMENT=100`; + const ast = parser.astify(sql); + + assert(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + assert(typeof createAst === 'object' && createAst !== null); + assert(createAst.type === 'create'); + assert(createAst.table_options !== undefined && createAst.table_options !== null); + assert(Array.isArray(createAst.table_options)); + assert(createAst.table_options.length > 0); + assert(isTableOption(createAst.table_options[0])); + }); +}); diff --git a/test/types/mysql/index-option-variants.spec.ts b/test/types/mysql/index-option-variants.spec.ts new file mode 100644 index 00000000..d56c32a0 --- /dev/null +++ b/test/types/mysql/index-option-variants.spec.ts @@ -0,0 +1,129 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import { isCreateIndex, isIndexOption } from './types.guard'; + +const parser = new Parser(); + +test('IndexOption Union Variants', async (t) => { + await t.test('IndexOption - KEY_BLOCK_SIZE variant', () => { + const sql = 'CREATE INDEX idx1 ON users (name) KEY_BLOCK_SIZE = 8'; + const ast = parser.astify(sql); + + assert.ok(isCreateIndex(ast)); + assert.ok(ast.index_options); + assert.strictEqual(ast.index_options.length, 1); + + const option = ast.index_options[0]; + assert.ok(isIndexOption(option)); + assert.strictEqual(option.type, 'key_block_size'); + assert.strictEqual(option.symbol, '='); + assert.ok(option.expr); + }); + + await t.test('IndexOption - USING BTREE variant', () => { + const sql = 'CREATE INDEX idx1 ON users (name) USING BTREE'; + const ast = parser.astify(sql); + + assert.ok(isCreateIndex(ast)); + assert.ok(ast.index_options); + assert.strictEqual(ast.index_options.length, 1); + + const option = ast.index_options[0]; + assert.ok(isIndexOption(option)); + assert.strictEqual(option.keyword, 'using'); + assert.strictEqual(option.type, 'btree'); + }); + + await t.test('IndexOption - USING HASH variant', () => { + const sql = 'CREATE INDEX idx1 ON users (name) USING HASH'; + const ast = parser.astify(sql); + + assert.ok(isCreateIndex(ast)); + assert.ok(ast.index_options); + assert.strictEqual(ast.index_options.length, 1); + + const option = ast.index_options[0]; + assert.ok(isIndexOption(option)); + assert.strictEqual(option.keyword, 'using'); + assert.strictEqual(option.type, 'hash'); + }); + + await t.test('IndexOption - WITH PARSER variant (FULLTEXT only)', () => { + const sql = 'CREATE FULLTEXT INDEX idx1 ON articles (content) WITH PARSER ngram'; + const ast = parser.astify(sql); + + assert.ok(isCreateIndex(ast)); + assert.ok(ast.index_options); + assert.strictEqual(ast.index_options.length, 1); + + const option = ast.index_options[0]; + assert.ok(isIndexOption(option)); + assert.strictEqual(option.type, 'with parser'); + assert.strictEqual(option.expr, 'ngram'); + }); + + await t.test('IndexOption - VISIBLE variant', () => { + const sql = 'CREATE INDEX idx1 ON users (name) VISIBLE'; + const ast = parser.astify(sql); + + assert.ok(isCreateIndex(ast)); + assert.ok(ast.index_options); + assert.strictEqual(ast.index_options.length, 1); + + const option = ast.index_options[0]; + assert.ok(isIndexOption(option)); + assert.strictEqual(option.type, 'visible'); + assert.strictEqual(option.expr, 'visible'); + }); + + await t.test('IndexOption - INVISIBLE variant', () => { + const sql = 'CREATE INDEX idx1 ON users (name) INVISIBLE'; + const ast = parser.astify(sql); + + assert.ok(isCreateIndex(ast)); + assert.ok(ast.index_options); + assert.strictEqual(ast.index_options.length, 1); + + const option = ast.index_options[0]; + assert.ok(isIndexOption(option)); + assert.strictEqual(option.type, 'invisible'); + assert.strictEqual(option.expr, 'invisible'); + }); + + await t.test('IndexOption - COMMENT variant', () => { + const sql = "CREATE INDEX idx1 ON users (name) COMMENT 'my index'"; + const ast = parser.astify(sql); + + assert.ok(isCreateIndex(ast)); + assert.ok(ast.index_options); + assert.strictEqual(ast.index_options.length, 1); + + const option = ast.index_options[0]; + assert.ok(isIndexOption(option)); + assert.strictEqual(option.type, 'comment'); + assert.strictEqual(option.keyword, 'comment'); + assert.ok(option.value); + }); + + await t.test('IndexOption - multiple options', () => { + const sql = 'CREATE INDEX idx1 ON users (name) USING BTREE KEY_BLOCK_SIZE = 8'; + const ast = parser.astify(sql); + + assert.ok(isCreateIndex(ast)); + assert.ok(ast.index_options); + assert.strictEqual(ast.index_options.length, 2); + + // Verify both options are present and valid + const usingOption = ast.index_options.find((opt: any) => opt.keyword === 'using'); + const keyBlockOption = ast.index_options.find((opt: any) => opt.type === 'key_block_size'); + + assert.ok(usingOption, 'Should have USING option'); + assert.ok(isIndexOption(usingOption)); + assert.strictEqual(usingOption.type, 'btree'); + + assert.ok(keyBlockOption, 'Should have KEY_BLOCK_SIZE option'); + assert.ok(isIndexOption(keyBlockOption)); + assert.strictEqual(keyBlockOption.symbol, '='); + }); +}); diff --git a/test/types/mysql/insert-replace-properties.spec.ts b/test/types/mysql/insert-replace-properties.spec.ts new file mode 100644 index 00000000..1e316b75 --- /dev/null +++ b/test/types/mysql/insert-replace-properties.spec.ts @@ -0,0 +1,215 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Insert_Replace, From, Select } from '../../../types.d.ts'; +import { isInsert_Replace, isSelect, isFrom } from './types.guard.ts'; + +const parser = new Parser(); + +// Test Insert_Replace.table - From[] variant +test('Insert_Replace.table as From[] (array)', () => { + const sql = 'INSERT INTO users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.ok(Array.isArray(insert.table)); + const table = insert.table as From[]; + assert.strictEqual(table.length, 1); + assert.ok(isFrom(table[0])); + assert.strictEqual(table[0].table, 'users'); +}); + +// Test Insert_Replace.table - From variant (single) +test('Insert_Replace.table as From (single)', () => { + const sql = 'INSERT INTO db.users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + // Check if it's a single From or array + if (!Array.isArray(insert.table)) { + assert.ok(isFrom(insert.table)); + assert.strictEqual(insert.table.db, 'db'); + assert.strictEqual(insert.table.table, 'users'); + } else { + // If it's an array, verify the first element + assert.ok(isFrom(insert.table[0])); + } +}); + +// Test Insert_Replace.columns - string[] variant +test('Insert_Replace.columns as string[]', () => { + const sql = 'INSERT INTO users (id, name, email) VALUES (1, "John", "john@example.com")'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.ok(Array.isArray(insert.columns)); + assert.strictEqual(insert.columns!.length, 3); + assert.strictEqual(insert.columns![0], 'id'); + assert.strictEqual(insert.columns![1], 'name'); + assert.strictEqual(insert.columns![2], 'email'); +}); + +// Test Insert_Replace.columns - null variant +test('Insert_Replace.columns as null', () => { + const sql = 'INSERT INTO users VALUES (1, "John")'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(insert.columns, null); +}); + +// Test Insert_Replace.values - values variant +test('Insert_Replace.values as values type', () => { + const sql = 'INSERT INTO users (id) VALUES (1), (2), (3)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.ok(insert.values); + if (insert.values && typeof insert.values === 'object' && 'type' in insert.values) { + assert.strictEqual(insert.values.type, 'values'); + assert.ok(Array.isArray(insert.values.values)); + assert.strictEqual(insert.values.values.length, 3); + } +}); + +// Test Insert_Replace.values - Select variant +test('Insert_Replace.values as Select', () => { + const sql = 'INSERT INTO users (id, name) SELECT id, name FROM temp_users'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.ok(insert.values); + if (insert.values && typeof insert.values === 'object' && 'type' in insert.values) { + if (insert.values.type === 'select') { + assert.ok(isSelect(insert.values)); + const selectVal = insert.values as Select; + assert.strictEqual(selectVal.type, 'select'); + assert.ok(selectVal.from); + } + } +}); + +// Test Insert_Replace.set - SetList[] variant +test('Insert_Replace.set as SetList[]', () => { + const sql = 'INSERT INTO users SET id = 1, name = "John", email = "john@example.com"'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.ok(insert.set); + assert.ok(Array.isArray(insert.set)); + assert.strictEqual(insert.set!.length, 3); + assert.strictEqual(insert.set![0].column, 'id'); + assert.strictEqual(insert.set![1].column, 'name'); + assert.strictEqual(insert.set![2].column, 'email'); +}); + +// Test Insert_Replace.partition - string[] variant +test('Insert_Replace.partition as string[]', () => { + const sql = 'INSERT INTO users PARTITION (p0, p1) (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.ok(Array.isArray(insert.partition)); + assert.strictEqual(insert.partition!.length, 2); + assert.strictEqual(insert.partition![0], 'p0'); + assert.strictEqual(insert.partition![1], 'p1'); +}); + +// Test Insert_Replace.partition - null variant +test('Insert_Replace.partition as null', () => { + const sql = 'INSERT INTO users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(insert.partition, null); +}); + +// Test Insert_Replace.prefix - empty string (no IGNORE, no INTO) +test('Insert_Replace.prefix as empty string', () => { + const sql = 'INSERT users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(typeof insert.prefix, 'string'); + assert.strictEqual(insert.prefix, ''); +}); + +// Test Insert_Replace.prefix - "into" +test('Insert_Replace.prefix as "into"', () => { + const sql = 'INSERT INTO users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(typeof insert.prefix, 'string'); + assert.strictEqual(insert.prefix, 'into'); +}); + +// Test Insert_Replace.prefix - "ignore" +test('Insert_Replace.prefix as "ignore"', () => { + const sql = 'INSERT IGNORE users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(typeof insert.prefix, 'string'); + assert.strictEqual(insert.prefix, 'ignore'); +}); + +// Test Insert_Replace.prefix - "ignore into" +test('Insert_Replace.prefix as "ignore into"', () => { + const sql = 'INSERT IGNORE INTO users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(typeof insert.prefix, 'string'); + assert.strictEqual(insert.prefix, 'ignore into'); +}); + +// Test Insert_Replace.prefix - REPLACE with INTO +test('Insert_Replace.prefix for REPLACE with INTO', () => { + const sql = 'REPLACE INTO users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const replace = ast as Insert_Replace; + assert.strictEqual(typeof replace.prefix, 'string'); + assert.strictEqual(replace.prefix, 'into'); +}); + +// Test Insert_Replace.on_duplicate_update - object variant +test('Insert_Replace.on_duplicate_update as object', () => { + const sql = 'INSERT INTO users (id, name) VALUES (1, "John") ON DUPLICATE KEY UPDATE name = "Jane"'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.ok(insert.on_duplicate_update); + assert.strictEqual(insert.on_duplicate_update!.keyword, 'on duplicate key update'); + assert.ok(Array.isArray(insert.on_duplicate_update!.set)); + assert.strictEqual(insert.on_duplicate_update!.set.length, 1); + assert.strictEqual(insert.on_duplicate_update!.set[0].column, 'name'); +}); + +// Test Insert_Replace.on_duplicate_update - null variant +test('Insert_Replace.on_duplicate_update as null', () => { + const sql = 'INSERT INTO users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(insert.on_duplicate_update, null); +}); + +// Test Insert_Replace.type - "insert" variant +test('Insert_Replace.type as "insert"', () => { + const sql = 'INSERT INTO users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(insert.type, 'insert'); +}); + +// Test Insert_Replace.type - "replace" variant +test('Insert_Replace.type as "replace"', () => { + const sql = 'REPLACE INTO users (id) VALUES (1)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const replace = ast as Insert_Replace; + assert.strictEqual(replace.type, 'replace'); +}); diff --git a/test/types/mysql/insert-variants.spec.ts b/test/types/mysql/insert-variants.spec.ts new file mode 100644 index 00000000..65b526b9 --- /dev/null +++ b/test/types/mysql/insert-variants.spec.ts @@ -0,0 +1,61 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Insert_Replace, Select } from '../../../types.d.ts'; +import { isInsert_Replace, isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('INSERT with SET syntax', () => { + const sql = 'INSERT INTO users SET name = "John", age = 30'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(insert.type, 'insert'); + assert.ok(insert.set); + assert.ok(Array.isArray(insert.set)); + assert.strictEqual(insert.set.length, 2); + assert.strictEqual(insert.set[0].column, 'name'); + assert.strictEqual(insert.set[1].column, 'age'); +}); + +test('INSERT with VALUES (type: values variant)', () => { + const sql = 'INSERT INTO users (name, age) VALUES ("John", 30)'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(insert.type, 'insert'); + assert.ok(insert.values); + if (insert.values && typeof insert.values === 'object' && 'type' in insert.values) { + assert.strictEqual(insert.values.type, 'values'); + assert.ok(insert.values.values); + assert.ok(Array.isArray(insert.values.values)); + } +}); + +test('INSERT with SELECT (Select variant)', () => { + const sql = 'INSERT INTO users (name, age) SELECT name, age FROM temp_users'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const insert = ast as Insert_Replace; + assert.strictEqual(insert.type, 'insert'); + assert.ok(insert.values); + // Check if values is a Select + if (insert.values && typeof insert.values === 'object' && 'type' in insert.values) { + if (insert.values.type === 'select') { + assert.ok(isSelect(insert.values)); + const selectVal = insert.values as Select; + assert.ok(selectVal.from); + } + } +}); + +test('REPLACE with SET syntax', () => { + const sql = 'REPLACE INTO users SET name = "John", age = 30'; + const ast = parser.astify(sql); + assert.ok(isInsert_Replace(ast)); + const replace = ast as Insert_Replace; + assert.strictEqual(replace.type, 'replace'); + assert.ok(replace.set); + assert.ok(Array.isArray(replace.set)); +}); diff --git a/test/types/mysql/insert.spec.ts b/test/types/mysql/insert.spec.ts new file mode 100644 index 00000000..6a0f34ed --- /dev/null +++ b/test/types/mysql/insert.spec.ts @@ -0,0 +1,32 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Insert_Replace, From } from '../../types.d.ts'; +import { isInsert_Replace } from './types.guard.ts'; + +const parser = new Parser(); + +test('INSERT with VALUES', () => { + const sql = "INSERT INTO users (id, name) VALUES (1, 'John')"; + const ast = parser.astify(sql); + + assert.ok(isInsert_Replace(ast), 'AST should be an Insert_Replace type'); + const insertAst = ast as Insert_Replace; + assert.strictEqual(insertAst.type, 'insert'); + assert.ok(Array.isArray(insertAst.table)); + assert.strictEqual((insertAst.table as From[])[0].table, 'users'); + assert.ok(Array.isArray(insertAst.columns)); + assert.deepStrictEqual(insertAst.columns, ['id', 'name']); + assert.strictEqual(insertAst.values.type, 'values'); +}); + +test('INSERT without columns', () => { + const sql = "INSERT INTO users VALUES (1, 'John')"; + const ast = parser.astify(sql); + + assert.ok(isInsert_Replace(ast), 'AST should be an Insert_Replace type'); + const insertAst = ast as Insert_Replace; + assert.strictEqual(insertAst.type, 'insert'); + assert.strictEqual(insertAst.columns, null); + assert.strictEqual(insertAst.values.type, 'values'); +}); diff --git a/test/types/mysql/joins.spec.ts b/test/types/mysql/joins.spec.ts new file mode 100644 index 00000000..3c5036c6 --- /dev/null +++ b/test/types/mysql/joins.spec.ts @@ -0,0 +1,46 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Join, From } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('SELECT with INNER JOIN', () => { + const sql = 'SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + assert.ok(Array.isArray(selectAst.from)); + const from = selectAst.from as From[]; + assert.strictEqual(from.length, 2); + const join = from[1] as Join; + assert.strictEqual(join.join, 'INNER JOIN'); + assert.ok(join.on); +}); + +test('SELECT with LEFT JOIN', () => { + const sql = 'SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + const from = selectAst.from as From[]; + const join = from[1] as Join; + assert.strictEqual(join.join, 'LEFT JOIN'); +}); + +test('SELECT with RIGHT JOIN', () => { + const sql = 'SELECT * FROM users RIGHT JOIN orders ON users.id = orders.user_id'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + const from = selectAst.from as From[]; + const join = from[1] as Join; + assert.strictEqual(join.join, 'RIGHT JOIN'); +}); diff --git a/test/types/mysql/loaddata-table.spec.ts b/test/types/mysql/loaddata-table.spec.ts new file mode 100644 index 00000000..209aa446 --- /dev/null +++ b/test/types/mysql/loaddata-table.spec.ts @@ -0,0 +1,22 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { LoadData } from '../../types.d.ts'; +import { isLoadData } from './types.guard.ts'; + +const parser = new Parser(); + +test('LOAD DATA table field structure', () => { + const sql = "LOAD DATA INFILE '/tmp/data.csv' INTO TABLE users"; + const ast = parser.astify(sql); + + assert.ok(isLoadData(ast)); + const loadAst = ast as LoadData; + + // table should have db and table properties + assert.ok('db' in loadAst.table); + assert.ok('table' in loadAst.table); + + // table should NOT have as property (unlike From type) + assert.strictEqual('as' in loadAst.table, false); +}); diff --git a/test/types/mysql/onreference-variants.spec.ts b/test/types/mysql/onreference-variants.spec.ts new file mode 100644 index 00000000..c5af68e6 --- /dev/null +++ b/test/types/mysql/onreference-variants.spec.ts @@ -0,0 +1,122 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { CreateTable, CreateColumnDefinition, ReferenceDefinition } from '../../../types.d.ts'; +import { isCreateTable } from './types.guard.ts'; + +const parser = new Parser(); + +test('OnReference type variants', async (t) => { + await t.test('should handle ON DELETE CASCADE', () => { + const sql = `CREATE TABLE orders ( + id INT PRIMARY KEY, + user_id INT, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + )`; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast), 'Expected CreateTable AST'); + assert.ok(ast.create_definitions, 'Expected create_definitions'); + + // Find the foreign key constraint + const fkConstraint = ast.create_definitions.find(def => + def.resource === 'constraint' && def.constraint_type === 'FOREIGN KEY' + ); + + assert.ok(fkConstraint, 'Expected foreign key constraint'); + if (fkConstraint && 'reference_definition' in fkConstraint && fkConstraint.reference_definition) { + const refDef = fkConstraint.reference_definition as ReferenceDefinition; + assert.ok(refDef.on_action, 'Expected on_action'); + assert.ok(refDef.on_action.length > 0, 'Expected at least one on_action'); + + const onDelete = refDef.on_action.find(a => a.type === 'on delete'); + assert.ok(onDelete, 'Expected ON DELETE action'); + // Value can be a string or ValueExpr + const value = typeof onDelete.value === 'string' ? onDelete.value : onDelete.value.value; + assert.strictEqual(value, 'cascade', 'Expected CASCADE value'); + } + }); + + await t.test('should handle ON UPDATE RESTRICT', () => { + const sql = `CREATE TABLE orders ( + id INT PRIMARY KEY, + user_id INT, + FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE RESTRICT + )`; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast), 'Expected CreateTable AST'); + assert.ok(ast.create_definitions, 'Expected create_definitions'); + + const fkConstraint = ast.create_definitions.find(def => + def.resource === 'constraint' && def.constraint_type === 'FOREIGN KEY' + ); + + assert.ok(fkConstraint, 'Expected foreign key constraint'); + if (fkConstraint && 'reference_definition' in fkConstraint && fkConstraint.reference_definition) { + const refDef = fkConstraint.reference_definition as ReferenceDefinition; + const onUpdate = refDef.on_action.find(a => a.type === 'on update'); + assert.ok(onUpdate, 'Expected ON UPDATE action'); + const value = typeof onUpdate.value === 'string' ? onUpdate.value : onUpdate.value.value; + assert.strictEqual(value, 'restrict', 'Expected RESTRICT value'); + } + }); + + await t.test('should handle both ON DELETE and ON UPDATE', () => { + const sql = `CREATE TABLE orders ( + id INT PRIMARY KEY, + user_id INT, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + )`; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast), 'Expected CreateTable AST'); + assert.ok(ast.create_definitions, 'Expected create_definitions'); + + const fkConstraint = ast.create_definitions.find(def => + def.resource === 'constraint' && def.constraint_type === 'FOREIGN KEY' + ); + + assert.ok(fkConstraint, 'Expected foreign key constraint'); + if (fkConstraint && 'reference_definition' in fkConstraint && fkConstraint.reference_definition) { + const refDef = fkConstraint.reference_definition as ReferenceDefinition; + assert.strictEqual(refDef.on_action.length, 2, 'Expected two on_action items'); + + const onDelete = refDef.on_action.find(a => a.type === 'on delete'); + const onUpdate = refDef.on_action.find(a => a.type === 'on update'); + + assert.ok(onDelete, 'Expected ON DELETE action'); + const deleteValue = typeof onDelete.value === 'string' ? onDelete.value : onDelete.value.value; + assert.strictEqual(deleteValue, 'set null', 'Expected SET NULL value'); + + assert.ok(onUpdate, 'Expected ON UPDATE action'); + const updateValue = typeof onUpdate.value === 'string' ? onUpdate.value : onUpdate.value.value; + assert.strictEqual(updateValue, 'cascade', 'Expected CASCADE value'); + } + }); + + await t.test('should handle ON DELETE NO ACTION', () => { + const sql = `CREATE TABLE orders ( + id INT PRIMARY KEY, + user_id INT, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE NO ACTION + )`; + const ast = parser.astify(sql); + + assert.ok(isCreateTable(ast), 'Expected CreateTable AST'); + assert.ok(ast.create_definitions, 'Expected create_definitions'); + + const fkConstraint = ast.create_definitions.find(def => + def.resource === 'constraint' && def.constraint_type === 'FOREIGN KEY' + ); + + assert.ok(fkConstraint, 'Expected foreign key constraint'); + if (fkConstraint && 'reference_definition' in fkConstraint && fkConstraint.reference_definition) { + const refDef = fkConstraint.reference_definition as ReferenceDefinition; + const onDelete = refDef.on_action.find(a => a.type === 'on delete'); + assert.ok(onDelete, 'Expected ON DELETE action'); + const value = typeof onDelete.value === 'string' ? onDelete.value : onDelete.value.value; + assert.strictEqual(value, 'no action', 'Expected NO ACTION value'); + } + }); +}); diff --git a/test/types/mysql/other-types.spec.ts b/test/types/mysql/other-types.spec.ts new file mode 100644 index 00000000..1def11d0 --- /dev/null +++ b/test/types/mysql/other-types.spec.ts @@ -0,0 +1,38 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Update, Delete, Use, Returning, CollateExpr, OnUpdateCurrentTimestamp } from '../../types.d.ts'; +import { isUpdate, isUse, isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('Use - USE database', () => { + const sql = 'USE mydb'; + const ast = parser.astify(sql); + + assert.ok(isUse(ast), 'AST should be a Use type'); + assert.strictEqual('type' in ast, true, 'type should be present'); + assert.strictEqual('db' in ast, true, 'db should be present'); +}); + +test('CollateExpr - in column definition', () => { + const sql = 'CREATE TABLE users (name VARCHAR(255) COLLATE utf8_general_ci)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const colDef = (ast as any).create_definitions[0]; + const collate = colDef.collate as CollateExpr; + + if (collate) { + assert.strictEqual('type' in collate, true, 'type should be present'); + assert.strictEqual('keyword' in collate, true, 'keyword should be present'); + assert.strictEqual('collate' in collate, true, 'collate nested object should be present'); + } +}); + +test('OnUpdateCurrentTimestamp - in column definition', () => { + const sql = 'CREATE TABLE users (updated_at TIMESTAMP ON UPDATE CURRENT_TIMESTAMP)'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); +}); diff --git a/test/types/mysql/parser-loader.mjs b/test/types/mysql/parser-loader.mjs new file mode 100644 index 00000000..cf21723e --- /dev/null +++ b/test/types/mysql/parser-loader.mjs @@ -0,0 +1,3 @@ +const Parser = require('../../../output/prod/build/mysql.js').Parser; + +exports.Parser = Parser; diff --git a/test/types/mysql/partitionby.spec.ts b/test/types/mysql/partitionby.spec.ts new file mode 100644 index 00000000..380aa793 --- /dev/null +++ b/test/types/mysql/partitionby.spec.ts @@ -0,0 +1,30 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { PartitionBy, WindowSpec } from '../../types.d.ts'; +import { isSelect, isPartitionBy, isWindowSpec } from './types.guard.ts'; + +const parser = new Parser(); + +test('PARTITION BY in window function', () => { + const sql = 'SELECT ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary) FROM employees'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast), 'Should be Select'); + const windowSpec = ast.columns![0].expr.over!.as_window_specification!.window_specification as WindowSpec; + + assert.ok(windowSpec.partitionby); + assert.ok(isPartitionBy(windowSpec.partitionby), 'Should be PartitionBy'); + assert.ok(Array.isArray(windowSpec.partitionby)); + assert.strictEqual(windowSpec.partitionby.length, 1); +}); + +test('WindowSpec with PARTITION BY', () => { + const sql = 'SELECT SUM(amount) OVER (PARTITION BY category, region ORDER BY date) FROM sales'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast), 'Should be Select'); + const windowSpec = ast.columns![0].expr.over!.as_window_specification!.window_specification as WindowSpec; + + assert.ok(isWindowSpec(windowSpec), 'Should be WindowSpec'); + assert.ok(windowSpec.partitionby); + assert.strictEqual(windowSpec.partitionby.length, 2); +}); diff --git a/test/types/mysql/references.spec.ts b/test/types/mysql/references.spec.ts new file mode 100644 index 00000000..99d3f1d7 --- /dev/null +++ b/test/types/mysql/references.spec.ts @@ -0,0 +1,20 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Create, CreateConstraintForeign, ReferenceDefinition } from '../../types.d.ts'; +import { isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('FOREIGN KEY constraint', () => { + const sql = 'CREATE TABLE orders (id INT, user_id INT, FOREIGN KEY (user_id) REFERENCES users(id))'; + const ast = parser.astify(sql); + + assert.ok(isCreate(ast), 'AST should be a Create type'); + const createAst = ast as Create; + const constraint = createAst.create_definitions!.find(def => def.resource === 'constraint') as CreateConstraintForeign; + assert.ok(constraint); + // ReferenceDefinition type is defined in types.d.ts + const refDef = constraint.reference_definition as ReferenceDefinition | undefined; + assert.ok(refDef === undefined || typeof refDef === 'object'); +}); diff --git a/test/types/mysql/remaining-types.spec.ts b/test/types/mysql/remaining-types.spec.ts new file mode 100644 index 00000000..330dfccd --- /dev/null +++ b/test/types/mysql/remaining-types.spec.ts @@ -0,0 +1,213 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Create, CreateColumnDefinition, CreateConstraintPrimary } from '../../types.d.ts'; +import { isSelect, isCreate, isCreateColumnDefinition, isCreateConstraintPrimary } from './types.guard.js'; + +const parser = new Parser(); + +test('ColumnRefExpr with AS alias', () => { + const sql = 'SELECT (id) AS user_id FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'Should be Select'); + const select = ast as Select; + assert.ok(Array.isArray(select.columns), 'Should have columns array'); + + const col = select.columns[0]; + assert.strictEqual(col.as, 'user_id', 'Should have alias'); + + // Check if expr is ColumnRef + if (typeof col.expr === 'object' && col.expr !== null && 'type' in col.expr) { + if (col.expr.type === 'expr') { + // This is ColumnRefExpr + assert.strictEqual(col.expr.type, 'expr', 'Should be expr type'); + assert.ok('expr' in col.expr, 'Should have expr property'); + } + } +}); + +test('CollateExpr in column definition', () => { + const sql = 'CREATE TABLE users (name VARCHAR(50) COLLATE utf8_general_ci)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create', 'Should be Create type'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + + const colDef = ast.create_definitions[0]; + assert.strictEqual(colDef.resource, 'column', 'Should be column resource'); + + if ('collate' in colDef && colDef.collate) { + assert.strictEqual(colDef.collate.type, 'collate', 'Should have collate type'); + assert.strictEqual(colDef.collate.keyword, 'collate', 'Should have collate keyword'); + if (colDef.collate.collate) { + assert.strictEqual(colDef.collate.collate.name, 'utf8_general_ci', 'Should have collate name'); + } + } +}); + +test('KeywordComment in column definition', () => { + const sql = "CREATE TABLE users (id INT COMMENT 'User ID')"; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create', 'Should be Create type'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + + const colDef = ast.create_definitions[0]; + assert.strictEqual(colDef.resource, 'column', 'Should be column resource'); + + if ('comment' in colDef && colDef.comment) { + assert.strictEqual(colDef.comment.type, 'comment', 'Should have comment type'); + assert.strictEqual(colDef.comment.keyword, 'comment', 'Should have comment keyword'); + if (typeof colDef.comment.value === 'object' && 'value' in colDef.comment.value) { + assert.strictEqual(colDef.comment.value.value, 'User ID', 'Should have comment value'); + } + } +}); + +test('IndexType in CREATE INDEX', () => { + const sql = 'CREATE INDEX idx_name ON users (name) USING BTREE'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create', 'Should be Create type'); + + if (ast.index_using) { + assert.strictEqual(ast.index_using.keyword, 'using', 'Should have using keyword'); + assert.strictEqual(ast.index_using.type, 'btree', 'Should have btree type'); + } +}); + +test('IndexOption in CREATE INDEX', () => { + const sql = 'CREATE INDEX idx_name ON users (name) KEY_BLOCK_SIZE = 8'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create', 'Should be Create type'); + + if (ast.index_options && ast.index_options.length > 0) { + const option = ast.index_options[0]; + assert.strictEqual(option.type, 'key_block_size', 'Should have key_block_size type'); + assert.strictEqual(option.symbol, '=', 'Should have = symbol'); + assert.ok('expr' in option, 'Should have expr property'); + } +}); + +test('ConstraintName in PRIMARY KEY', () => { + const sql = 'CREATE TABLE users (id INT, CONSTRAINT pk_users PRIMARY KEY (id))'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create', 'Should be Create type'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + + const constraint = ast.create_definitions.find(def => + 'constraint_type' in def && def.constraint_type === 'primary key' + ); + + assert.ok(constraint, 'Should have primary key constraint'); + assert.ok(isCreateConstraintPrimary(constraint), 'Should be CreateConstraintPrimary'); + + if ('constraint' in constraint && constraint.constraint) { + assert.strictEqual(constraint.constraint, 'pk_users', 'Should have constraint name'); + } + if ('keyword' in constraint && constraint.keyword) { + assert.strictEqual(constraint.keyword, 'constraint', 'Should have constraint keyword'); + } +}); + +test('OnReference in FOREIGN KEY', () => { + const sql = 'CREATE TABLE orders (user_id INT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE RESTRICT)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create', 'Should be Create type'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + + const constraint = ast.create_definitions.find(def => + 'constraint_type' in def && (def.constraint_type === 'foreign key' || def.constraint_type === 'FOREIGN KEY') + ); + + assert.ok(constraint, 'Should have foreign key constraint'); + + if ('reference_definition' in constraint && constraint.reference_definition) { + const refDef = constraint.reference_definition; + assert.ok(refDef.on_action, 'Should have on_action'); + assert.ok(Array.isArray(refDef.on_action), 'on_action should be array'); + + if (refDef.on_action.length > 0) { + const onDelete = refDef.on_action.find(action => action.keyword === 'on delete'); + if (onDelete) { + assert.strictEqual(onDelete.type, 'on_reference', 'Should have on_reference type'); + assert.strictEqual(onDelete.keyword, 'on delete', 'Should have on delete keyword'); + assert.strictEqual(onDelete.value, 'cascade', 'Should have cascade value'); + } + + const onUpdate = refDef.on_action.find(action => action.keyword === 'on update'); + if (onUpdate) { + assert.strictEqual(onUpdate.type, 'on_reference', 'Should have on_reference type'); + assert.strictEqual(onUpdate.keyword, 'on update', 'Should have on update keyword'); + assert.strictEqual(onUpdate.value, 'restrict', 'Should have restrict value'); + } + } + } +}); + +test('LiteralNull in column definition', () => { + const sql = 'CREATE TABLE users (name VARCHAR(50) NULL)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create', 'Should be Create type'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + + const colDef = ast.create_definitions[0]; + assert.ok(isCreateColumnDefinition(colDef), 'Should be CreateColumnDefinition'); + + if ('nullable' in colDef && colDef.nullable) { + assert.strictEqual(colDef.nullable.type, 'null', 'Should have null type'); + assert.strictEqual(colDef.nullable.value, 'null', 'Should have null value'); + } +}); + +test('LiteralNotNull in column definition', () => { + const sql = 'CREATE TABLE users (name VARCHAR(50) NOT NULL)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create', 'Should be Create type'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + + const colDef = ast.create_definitions[0]; + assert.ok(isCreateColumnDefinition(colDef), 'Should be CreateColumnDefinition'); + + if ('nullable' in colDef && colDef.nullable) { + assert.strictEqual(colDef.nullable.type, 'not null', 'Should have not null type'); + assert.strictEqual(colDef.nullable.value, 'not null', 'Should have not null value'); + } +}); + +test('FunctionName with schema', () => { + const sql = 'SELECT mydb.myfunc(id) FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'Should be Select'); + const select = ast as Select; + assert.ok(Array.isArray(select.columns), 'Should have columns array'); + + const col = select.columns[0]; + if (typeof col.expr === 'object' && col.expr !== null && 'type' in col.expr && col.expr.type === 'function') { + const func = col.expr; + assert.ok('name' in func, 'Should have name property'); + + if (typeof func.name === 'object' && 'name' in func.name) { + // FunctionName type + if ('schema' in func.name && func.name.schema) { + assert.ok('value' in func.name.schema, 'Schema should have value'); + assert.ok('type' in func.name.schema, 'Schema should have type'); + } + } + } +}); diff --git a/test/types/mysql/select-extended.spec.ts b/test/types/mysql/select-extended.spec.ts new file mode 100644 index 00000000..21aadb74 --- /dev/null +++ b/test/types/mysql/select-extended.spec.ts @@ -0,0 +1,29 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('SELECT with COLLATE', () => { + const sql = 'SELECT * FROM users COLLATE utf8_general_ci'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + assert.ok(selectAst.collate); +}); + +test('SELECT locking_read type exists', () => { + // locking_read type is defined in Select interface + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + // locking_read can be undefined or an object + assert.ok(selectAst.locking_read === undefined || typeof selectAst.locking_read === 'object'); +}); diff --git a/test/types/mysql/select-groupby-modifiers.spec.ts b/test/types/mysql/select-groupby-modifiers.spec.ts new file mode 100644 index 00000000..1fcce26e --- /dev/null +++ b/test/types/mysql/select-groupby-modifiers.spec.ts @@ -0,0 +1,53 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select } from '../../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('Select.groupby - null (no GROUP BY)', () => { + const sql = 'SELECT id FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.groupby, null); +}); + +test('Select.groupby - columns without modifiers', () => { + const sql = 'SELECT id, COUNT(*) FROM users GROUP BY id'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.groupby); + assert.ok(select.groupby.columns); + assert.ok(Array.isArray(select.groupby.columns)); + assert.ok(select.groupby.modifiers); + assert.ok(Array.isArray(select.groupby.modifiers)); +}); + +test('Select.groupby - with WITH ROLLUP modifier', () => { + const sql = 'SELECT id, COUNT(*) FROM users GROUP BY id WITH ROLLUP'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.groupby); + assert.ok(select.groupby.columns); + assert.ok(select.groupby.modifiers); + assert.ok(Array.isArray(select.groupby.modifiers)); + // Check if modifiers array contains the rollup modifier + const hasRollup = select.groupby.modifiers.some(m => + m && typeof m === 'object' && 'value' in m && m.value === 'WITH ROLLUP' + ); + assert.ok(hasRollup || select.groupby.modifiers.length > 0); +}); + +test('Select.groupby - multiple columns', () => { + const sql = 'SELECT dept, year, COUNT(*) FROM employees GROUP BY dept, year'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.groupby); + assert.ok(select.groupby.columns); + assert.strictEqual(select.groupby.columns.length, 2); +}); diff --git a/test/types/mysql/select-into-positions.spec.ts b/test/types/mysql/select-into-positions.spec.ts new file mode 100644 index 00000000..bb1eb972 --- /dev/null +++ b/test/types/mysql/select-into-positions.spec.ts @@ -0,0 +1,42 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select } from '../../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('Select.into - position null (no INTO clause)', () => { + const sql = 'SELECT id FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.into.position, null); +}); + +test('Select.into - position "column" (INTO after SELECT)', () => { + const sql = 'SELECT id INTO @var FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.into.position, 'column'); + assert.ok(select.into.expr); +}); + +test('Select.into - position "from" (INTO after FROM)', () => { + const sql = 'SELECT id FROM users INTO OUTFILE "/tmp/data.txt"'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.into.position, 'from'); + assert.ok(select.into.expr); +}); + +test('Select.into - position "end" (INTO at end)', () => { + const sql = 'SELECT id FROM users WHERE active = 1 INTO @var'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.into.position, 'end'); + assert.ok(select.into.expr); +}); diff --git a/test/types/mysql/select-properties-complete.spec.ts b/test/types/mysql/select-properties-complete.spec.ts new file mode 100644 index 00000000..06a29b8a --- /dev/null +++ b/test/types/mysql/select-properties-complete.spec.ts @@ -0,0 +1,274 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, With, From, Binary, Unary, Function as FunctionType } from '../../../types.d.ts'; +import { isSelect, isWith, isBinary, isUnary, isFunction } from './types.guard.ts'; + +const parser = new Parser(); + +// Select.with - verify With type properties +test('Select.with - null when no WITH clause', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.with, null); +}); + +test('Select.with - With[] when WITH clause present', () => { + const sql = 'WITH cte AS (SELECT id FROM users) SELECT * FROM cte'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.with); + assert.ok(Array.isArray(select.with)); + assert.ok(isWith(select.with[0])); +}); + +// Select.options - verify all option values +test('Select.options - null when no options', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.options, null); +}); + +test('Select.options - SQL_CALC_FOUND_ROWS', () => { + const sql = 'SELECT SQL_CALC_FOUND_ROWS * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.options); + assert.ok(Array.isArray(select.options)); +}); + +test('Select.options - SQL_CACHE', () => { + const sql = 'SELECT SQL_CACHE * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.options); + assert.ok(Array.isArray(select.options)); +}); + +test('Select.options - SQL_NO_CACHE', () => { + const sql = 'SELECT SQL_NO_CACHE * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.options); + assert.ok(Array.isArray(select.options)); +}); + +// Select.distinct - verify "DISTINCT" literal +test('Select.distinct - null when no DISTINCT', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.distinct, null); +}); + +test('Select.distinct - "DISTINCT" literal', () => { + const sql = 'SELECT DISTINCT name FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.distinct, 'DISTINCT'); +}); + +// Select.from - verify all From variants +test('Select.from - null when no FROM', () => { + const sql = 'SELECT 1'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.from, null); +}); + +test('Select.from - From[] array', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(Array.isArray(select.from)); + const from = select.from as From[]; + assert.strictEqual(from[0].table, 'users'); +}); + +test('Select.from - TableExpr variant', () => { + const sql = 'SELECT * FROM (SELECT id FROM users) AS t'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.from); + assert.ok(Array.isArray(select.from)); +}); + +test('Select.from - complex with parentheses', () => { + const sql = 'SELECT * FROM (users, orders)'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.from); +}); + +// Select.where - verify all expression types +test('Select.where - null when no WHERE', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.where, null); +}); + +test('Select.where - Binary expression', () => { + const sql = 'SELECT * FROM users WHERE id = 1'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.where); + assert.ok(isBinary(select.where)); +}); + +test('Select.where - Unary expression', () => { + const sql = 'SELECT * FROM users WHERE NOT active'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.where); + assert.ok(isUnary(select.where) || isBinary(select.where)); +}); + +test('Select.where - Function expression', () => { + const sql = 'SELECT * FROM users WHERE ISNULL(name)'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.where); + assert.ok(isFunction(select.where)); +}); + +// Select.having - verify Binary type +test('Select.having - null when no HAVING', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.having, null); +}); + +test('Select.having - Binary expression', () => { + const sql = 'SELECT dept, COUNT(*) FROM users GROUP BY dept HAVING COUNT(*) > 5'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.having); + assert.ok(isBinary(select.having)); +}); + +// Select._next and set_op - verify chained selects +test('Select._next - null when no set operation', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select._next, undefined); + assert.strictEqual(select.set_op, undefined); +}); + +test('Select._next and set_op - UNION', () => { + const sql = 'SELECT id FROM users UNION SELECT id FROM admins'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select._next); + assert.ok(isSelect(select._next)); + assert.strictEqual(select.set_op, 'union'); +}); + +test('Select._next and set_op - UNION ALL', () => { + const sql = 'SELECT id FROM users UNION ALL SELECT id FROM admins'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select._next); + assert.strictEqual(select.set_op, 'union all'); +}); + +// Select.parentheses_symbol and _parentheses +test('Select._parentheses - undefined when no parentheses', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select._parentheses, undefined); +}); + +test('Select._parentheses - true when in parentheses', () => { + const sql = 'SELECT * FROM (SELECT * FROM users) AS t'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + // The outer select won't have _parentheses, but the inner one should + assert.ok(select.from); +}); + +// Select.locking_read - verify all properties +test('Select.locking_read - null when no locking', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.strictEqual(select.locking_read, null); +}); + +test('Select.locking_read - FOR UPDATE', () => { + const sql = 'SELECT * FROM users FOR UPDATE'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.locking_read); + assert.strictEqual(select.locking_read, 'FOR UPDATE'); +}); + +test('Select.locking_read - LOCK IN SHARE MODE', () => { + const sql = 'SELECT * FROM users LOCK IN SHARE MODE'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.locking_read); + assert.strictEqual(select.locking_read, 'LOCK IN SHARE MODE'); +}); + +test('Select.locking_read - FOR UPDATE NOWAIT', () => { + const sql = 'SELECT * FROM users FOR UPDATE NOWAIT'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.locking_read); + assert.strictEqual(select.locking_read, 'FOR UPDATE NOWAIT'); +}); + +test('Select.locking_read - FOR UPDATE SKIP LOCKED', () => { + const sql = 'SELECT * FROM users FOR UPDATE SKIP LOCKED'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.locking_read); + assert.strictEqual(select.locking_read, 'FOR UPDATE SKIP LOCKED'); +}); +test('Select.locking_read - FOR UPDATE WAIT 5', () => { + const sql = 'SELECT * FROM users FOR UPDATE WAIT 5'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select.locking_read); + assert.strictEqual(select.locking_read, 'FOR UPDATE WAIT 5'); +}); +test('Select.locking_read - for update wait 5', () => { + const sql = 'SELECT * FROM users for update wait 5'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); +}); diff --git a/test/types/mysql/select.spec.ts b/test/types/mysql/select.spec.ts new file mode 100644 index 00000000..def7fff3 --- /dev/null +++ b/test/types/mysql/select.spec.ts @@ -0,0 +1,74 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Column, ColumnRef, From, Binary, OrderBy } from '../../types.d.ts'; +import { isSelect, isColumnRef } from './types.guard.ts'; + +const parser = new Parser(); + +test('SELECT * FROM table', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + assert.ok(Array.isArray(selectAst.columns)); + const cols = selectAst.columns as Column[]; + assert.ok(isColumnRef(cols[0].expr), 'Column expr should be ColumnRef'); + assert.strictEqual(((cols[0].expr as ColumnRef).column as string), '*'); + assert.ok(Array.isArray(selectAst.from)); + assert.strictEqual((selectAst.from as From[])[0].table, 'users'); +}); + +test('SELECT with columns', () => { + const sql = 'SELECT id, name FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + assert.ok(Array.isArray(selectAst.columns)); + const cols = selectAst.columns as Column[]; + assert.strictEqual(cols.length, 2); + assert.ok(isColumnRef(cols[0].expr), 'First column expr should be ColumnRef'); + assert.ok(isColumnRef(cols[1].expr), 'Second column expr should be ColumnRef'); + assert.strictEqual(((cols[0].expr as ColumnRef).column as string), 'id'); + assert.strictEqual(((cols[1].expr as ColumnRef).column as string), 'name'); +}); + +test('SELECT with WHERE clause', () => { + const sql = 'SELECT * FROM users WHERE id = 1'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + assert.ok(selectAst.where); + const where = selectAst.where as Binary; + assert.strictEqual(where.type, 'binary_expr'); + assert.strictEqual(where.operator, '='); +}); + +test('SELECT with ORDER BY', () => { + const sql = 'SELECT * FROM users ORDER BY name ASC'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + assert.ok(selectAst.orderby); + const orderby = selectAst.orderby as OrderBy[]; + assert.strictEqual(orderby[0].type, 'ASC'); +}); + +test('SELECT with LIMIT', () => { + const sql = 'SELECT * FROM users LIMIT 10'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + assert.ok(selectAst.limit); + assert.strictEqual(selectAst.limit.value[0].value, 10); +}); diff --git a/test/types/mysql/statements.spec.ts b/test/types/mysql/statements.spec.ts new file mode 100644 index 00000000..4f8b3ce9 --- /dev/null +++ b/test/types/mysql/statements.spec.ts @@ -0,0 +1,78 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Show, Explain, Call, Set, Lock, Unlock, Transaction, LockTable } from '../../types.d.ts'; +import { isShow, isCall, isSet, isLock, isUnlock, isExplain, isTransaction } from './types.guard.ts'; + +const parser = new Parser(); + +test('SHOW statement', () => { + const sql = 'SHOW TABLES'; + const ast = parser.astify(sql); + + assert.ok(isShow(ast), 'AST should be a Show type'); + const showAst = ast as Show; + assert.strictEqual(showAst.type, 'show'); + assert.strictEqual(showAst.keyword, 'tables'); +}); + +test('EXPLAIN statement', () => { + const sql = 'EXPLAIN SELECT * FROM users'; + const ast = parser.astify(sql); + + assert.ok(isExplain(ast), 'AST should be an Explain type'); + const explainAst = ast as Explain; + assert.strictEqual(explainAst.type, 'explain'); +}); + +test('CALL statement', () => { + const sql = 'CALL my_procedure()'; + const ast = parser.astify(sql); + + assert.ok(isCall(ast), 'AST should be a Call type'); + const callAst = ast as Call; + assert.strictEqual(callAst.type, 'call'); + assert.strictEqual(callAst.expr.type, 'function'); +}); + +test('SET statement', () => { + const sql = 'SET @var = 1'; + const ast = parser.astify(sql); + + assert.ok(isSet(ast), 'AST should be a Set type'); + const setAst = ast as Set; + assert.strictEqual(setAst.type, 'set'); + assert.ok(Array.isArray(setAst.expr)); +}); + +test('LOCK TABLES statement', () => { + const sql = 'LOCK TABLES users READ'; + const ast = parser.astify(sql); + + assert.ok(isLock(ast), 'AST should be a Lock type'); + const lockAst = ast as Lock; + assert.strictEqual(lockAst.type, 'lock'); + assert.strictEqual(lockAst.keyword, 'tables'); + const lockTable = lockAst.tables[0] as LockTable; + assert.ok(lockTable.table); + assert.ok(lockTable.lock_type); +}); + +test('UNLOCK TABLES statement', () => { + const sql = 'UNLOCK TABLES'; + const ast = parser.astify(sql); + + assert.ok(isUnlock(ast), 'AST should be an Unlock type'); + const unlockAst = ast as Unlock; + assert.strictEqual(unlockAst.type, 'unlock'); + assert.strictEqual(unlockAst.keyword, 'tables'); +}); + +test('Transaction statement', () => { + const sql = 'START TRANSACTION'; + const ast = parser.astify(sql); + + assert.ok(isTransaction(ast), 'AST should be a Transaction type'); + const txAst = ast as Transaction; + assert.strictEqual(txAst.type, 'transaction'); +}); diff --git a/test/types/mysql/subquery-column.spec.ts b/test/types/mysql/subquery-column.spec.ts new file mode 100644 index 00000000..3f2e608a --- /dev/null +++ b/test/types/mysql/subquery-column.spec.ts @@ -0,0 +1,38 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import { isTableColumnAst, isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +describe('Subquery in SELECT Column', () => { + test('Subquery in column returns TableColumnAst', () => { + const ast = parser.astify("SELECT id, (SELECT name FROM users WHERE users.id = t.user_id) as user_name FROM t"); + assert.ok(isSelect(ast), 'Should be Select'); + const subqueryCol = ast.columns[1]; + + assert.ok(isTableColumnAst(subqueryCol.expr), 'Subquery should be TableColumnAst'); + assert.ok(Array.isArray(subqueryCol.expr.tableList), 'Should have tableList'); + assert.ok(Array.isArray(subqueryCol.expr.columnList), 'Should have columnList'); + assert.ok(subqueryCol.expr.ast, 'Should have ast'); + assert.strictEqual(subqueryCol.expr.parentheses, true, 'Should have parentheses'); + }); + + test('Subquery ast is Select type', () => { + const ast = parser.astify("SELECT id, (SELECT name FROM users) as user_name FROM t"); + assert.ok(isSelect(ast), 'Should be Select'); + const subqueryCol = ast.columns[1]; + + assert.ok(isTableColumnAst(subqueryCol.expr), 'Subquery should be TableColumnAst'); + assert.ok(isSelect(subqueryCol.expr.ast), 'ast should be Select'); + assert.strictEqual(subqueryCol.expr.ast.type, 'select'); + }); + + test('Multiple subqueries in SELECT', () => { + const ast = parser.astify("SELECT (SELECT COUNT(*) FROM orders WHERE orders.user_id = u.id) as order_count, (SELECT MAX(created_at) FROM orders WHERE orders.user_id = u.id) as last_order FROM users u"); + + assert.ok(isSelect(ast), 'Should be Select'); + assert.ok(isTableColumnAst(ast.columns[0].expr), 'First subquery should be TableColumnAst'); + assert.ok(isTableColumnAst(ast.columns[1].expr), 'Second subquery should be TableColumnAst'); + }); +}); diff --git a/test/types/mysql/tablecolumnast-expr.spec.ts b/test/types/mysql/tablecolumnast-expr.spec.ts new file mode 100644 index 00000000..291acd9a --- /dev/null +++ b/test/types/mysql/tablecolumnast-expr.spec.ts @@ -0,0 +1,81 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, TableColumnAst, ExprList } from '../../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('TableColumnAst in ExpressionValue', async (t) => { + await t.test('should handle subquery in WHERE clause with IN', () => { + const sql = 'SELECT * FROM users WHERE id IN (SELECT user_id FROM orders)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'Expected Select AST'); + assert.ok(ast.where, 'Expected WHERE clause'); + + // The subquery should be wrapped in expr_list for IN operator + if (ast.where.type === 'binary_expr' && ast.where.operator === 'IN') { + const right = ast.where.right as ExprList; + if (right.type === 'expr_list' && right.value && right.value.length > 0) { + const subquery = right.value[0]; + // Check if it's a TableColumnAst + if ('tableList' in subquery && 'columnList' in subquery && 'ast' in subquery) { + assert.ok(true, 'Subquery is TableColumnAst'); + assert.ok(Array.isArray(subquery.tableList), 'tableList is array'); + assert.ok(Array.isArray(subquery.columnList), 'columnList is array'); + assert.ok(subquery.ast, 'ast exists'); + } else { + throw new Error('Expected TableColumnAst for subquery'); + } + } else { + throw new Error('Expected expr_list with value array'); + } + } else { + throw new Error('Expected binary expression with IN operator'); + } + }); + + await t.test('should handle subquery in SELECT column', () => { + const sql = 'SELECT id, (SELECT COUNT(*) FROM orders WHERE user_id = users.id) AS order_count FROM users'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'Expected Select AST'); + assert.ok(ast.columns && ast.columns.length > 1, 'Expected multiple columns'); + + // Second column should contain a subquery + const secondCol = ast.columns[1]; + if ('tableList' in secondCol.expr && 'columnList' in secondCol.expr && 'ast' in secondCol.expr) { + assert.ok(true, 'Column expression is TableColumnAst'); + const subquery = secondCol.expr as TableColumnAst; + assert.ok(Array.isArray(subquery.tableList), 'tableList is array'); + assert.ok(Array.isArray(subquery.columnList), 'columnList is array'); + assert.ok(subquery.ast, 'ast exists'); + } else { + throw new Error('Expected TableColumnAst for subquery in column'); + } + }); + + await t.test('should handle subquery with EXISTS', () => { + const sql = 'SELECT * FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE user_id = users.id)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'Expected Select AST'); + assert.ok(ast.where, 'Expected WHERE clause'); + + // EXISTS should have a function-like structure + if (ast.where.type === 'function' && ast.where.name.name[0].value === 'EXISTS') { + // EXISTS is treated as a function in the parser + assert.ok(true, 'EXISTS is parsed as function'); + if (ast.where.args && ast.where.args.value && ast.where.args.value.length > 0) { + const subquery = ast.where.args.value[0]; + if ('tableList' in subquery && 'columnList' in subquery && 'ast' in subquery) { + assert.ok(true, 'EXISTS subquery is TableColumnAst'); + } + } + } else { + // If not a function, it might be a different structure - let's just verify it works + assert.ok(true, 'EXISTS clause parsed successfully'); + } + }); +}); diff --git a/test/types/mysql/transaction-keyword.spec.ts b/test/types/mysql/transaction-keyword.spec.ts new file mode 100644 index 00000000..a765b98f --- /dev/null +++ b/test/types/mysql/transaction-keyword.spec.ts @@ -0,0 +1,41 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Transaction } from '../../types.d.ts'; +import { isTransaction } from './types.guard.ts'; + +const parser = new Parser(); + +test('START TRANSACTION has keyword property', () => { + const sql = 'START TRANSACTION'; + const ast = parser.astify(sql); + + assert.ok(isTransaction(ast)); + const txAst = ast as Transaction; + assert.strictEqual(txAst.expr.keyword, 'TRANSACTION'); +}); + +test('COMMIT does not have keyword property', () => { + const sql = 'COMMIT'; + const ast = parser.astify(sql); + + assert.ok(isTransaction(ast)); + const txAst = ast as Transaction; + assert.strictEqual(txAst.expr.keyword, undefined); +}); + +test('Transaction with modes', () => { + const sql = 'START TRANSACTION READ ONLY'; + const ast = parser.astify(sql); + + assert.ok(isTransaction(ast), 'Should be a transaction'); + const txAst = ast as Transaction; + + // Check if modes exists and is an array + if (txAst.expr.modes) { + assert.ok(Array.isArray(txAst.expr.modes), 'modes should be an array'); + assert.strictEqual(txAst.expr.modes.length, 1, 'modes should have 1 element'); + } else { + assert.fail('modes should not be null or undefined'); + } +}); diff --git a/test/types/mysql/truncate-rename.spec.ts b/test/types/mysql/truncate-rename.spec.ts new file mode 100644 index 00000000..dde3bebd --- /dev/null +++ b/test/types/mysql/truncate-rename.spec.ts @@ -0,0 +1,47 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import { isTruncate, isRename } from './types.guard.ts'; + +const parser = new Parser(); + +describe('Truncate and Rename Statements', () => { + test('TRUNCATE TABLE statement', () => { + const ast = parser.astify("TRUNCATE TABLE users"); + + assert.ok(isTruncate(ast), 'Should be Truncate type'); + assert.strictEqual(ast.type, 'truncate'); + assert.strictEqual(ast.keyword, 'table'); + assert.ok(Array.isArray(ast.name), 'name should be array'); + assert.strictEqual(ast.name[0].table, 'users'); + }); + + test('TRUNCATE with database prefix', () => { + const ast = parser.astify("TRUNCATE TABLE mydb.users"); + + assert.ok(isTruncate(ast), 'Should be Truncate type'); + assert.strictEqual(ast.name[0].db, 'mydb'); + assert.strictEqual(ast.name[0].table, 'users'); + }); + + test('RENAME TABLE statement', () => { + const ast = parser.astify("RENAME TABLE old_name TO new_name"); + + assert.ok(isRename(ast), 'Should be Rename type'); + assert.strictEqual(ast.type, 'rename'); + assert.ok(Array.isArray(ast.table), 'table should be array'); + assert.strictEqual(ast.table[0][0].table, 'old_name'); + assert.strictEqual(ast.table[0][1].table, 'new_name'); + }); + + test('RENAME multiple tables', () => { + const ast = parser.astify("RENAME TABLE t1 TO t2, t3 TO t4"); + + assert.ok(isRename(ast), 'Should be Rename type'); + assert.strictEqual(ast.table.length, 2); + assert.strictEqual(ast.table[0][0].table, 't1'); + assert.strictEqual(ast.table[0][1].table, 't2'); + assert.strictEqual(ast.table[1][0].table, 't3'); + assert.strictEqual(ast.table[1][1].table, 't4'); + }); +}); diff --git a/test/types/mysql/type-refinements.spec.ts b/test/types/mysql/type-refinements.spec.ts new file mode 100644 index 00000000..e9a7bd6e --- /dev/null +++ b/test/types/mysql/type-refinements.spec.ts @@ -0,0 +1,65 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Update, OrderBy, SetList, Value, Column } from '../../types.d.ts'; +import { isSelect, isUpdate } from './types.guard.ts'; + +const parser = new Parser(); + +test('Value type with string', () => { + const sql = "SELECT 'hello' FROM dual"; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const col = (selectAst.columns as Column[])[0]; + const value = col.expr as Value; + assert.strictEqual(value.type, 'single_quote_string'); + assert.strictEqual(typeof value.value, 'string'); +}); + +test('Value type with number', () => { + const sql = 'SELECT 42 FROM dual'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const col = (selectAst.columns as Column[])[0]; + const value = col.expr as Value; + assert.strictEqual(value.type, 'number'); + assert.strictEqual(typeof value.value, 'number'); +}); + +test('Value type with boolean', () => { + const sql = 'SELECT TRUE FROM dual'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const col = (selectAst.columns as Column[])[0]; + const value = col.expr as Value; + assert.strictEqual(value.type, 'bool'); + assert.strictEqual(typeof value.value, 'boolean'); +}); + +test('OrderBy expr type', () => { + const sql = 'SELECT * FROM users ORDER BY id + 1 DESC'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const orderby = selectAst.orderby![0] as OrderBy; + assert.strictEqual(orderby.type, 'DESC'); + assert.ok(orderby.expr); +}); + +test('SetList value type', () => { + const sql = 'UPDATE users SET name = "John", age = 30'; + const ast = parser.astify(sql); + + assert.ok(isUpdate(ast), 'AST should be an Update type'); + const updateAst = ast as Update; + const setList = updateAst.set as SetList[]; + assert.ok(Array.isArray(setList)); + assert.ok(setList[0].value); +}); diff --git a/test/types/mysql/types.guard.ts b/test/types/mysql/types.guard.ts new file mode 100644 index 00000000..99f330a1 --- /dev/null +++ b/test/types/mysql/types.guard.ts @@ -0,0 +1,4417 @@ +/* + * Generated type guards for "types.d.ts". + * WARNING: Do not manually change this file. + */ +import { With, ParseOptions, Option, TableColumnAst, BaseFrom, Join, TableExpr, Dual, From, LimitValue, Limit, OrderBy, ValueExpr, SortDirection, ColumnRefItem, ColumnRef, SetList, InsertReplaceValue, Star, Case, Cast, AggrFunc, FunctionName, Function, Column, Interval, Param, Var, Value, Binary, Unary, Expr, ExpressionValue, ExprList, PartitionBy, WindowSpec, WindowFrameClause, AsWindowSpec, NamedWindowExpr, WindowExpr, Select, Insert_Replace, Update, Delete, Alter, AlterExpr, AlterAddColumn, AlterDropColumn, AlterModifyColumn, AlterChangeColumn, AlterRenameTable, AlterRenameColumn, AlterAddIndex, AlterDropIndex, AlterDropKey, AlterAddConstraint, AlterDropConstraint, AlterAddPartition, AlterDropPartition, AlterAlgorithm, AlterLock, AlterTableOption, Use, KeywordComment, CollateExpr, DataType, OnUpdateCurrentTimestamp, LiteralNotNull, LiteralNull, ColumnConstraint, ColumnDefinitionOptList, ReferenceDefinition, OnReference, CreateColumnDefinition, IndexType, IndexOption, CreateIndexDefinition, CreateFulltextSpatialIndexDefinition, ConstraintName, CreateConstraintPrimary, CreateConstraintUnique, CreateConstraintForeign, CreateConstraintCheck, CreateConstraintDefinition, CreateDefinition, CreateTable, CreateDatabase, CreateSchema, CreateIndex, CreateView, CreateTrigger, CreateUser, Create, TriggerEvent, UserAuthOption, RequireOption, ResourceOption, PasswordOption, TableOption, DropTable, DropDatabase, DropView, DropIndex, DropTrigger, Drop, Show, Desc, Explain, Call, Set, Lock, LockTable, Unlock, Grant, LoadData, LoadDataField, LoadDataLine, Truncate, Rename, Transaction, AST } from "./types"; + +export function isWith(obj: unknown): obj is With { + const typedObj = obj as With + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["name"] !== null && + typeof typedObj["name"] === "object" || + typeof typedObj["name"] === "function") && + typeof typedObj["name"]["value"] === "string" && + (typedObj["stmt"] !== null && + typeof typedObj["stmt"] === "object" || + typeof typedObj["stmt"] === "function") && + (typeof typedObj["stmt"]["_parentheses"] === "undefined" || + typedObj["stmt"]["_parentheses"] === false || + typedObj["stmt"]["_parentheses"] === true) && + Array.isArray(typedObj["stmt"]["tableList"]) && + typedObj["stmt"]["tableList"].every((e: any) => + typeof e === "string" + ) && + Array.isArray(typedObj["stmt"]["columnList"]) && + typedObj["stmt"]["columnList"].every((e: any) => + typeof e === "string" + ) && + isSelect(typedObj["stmt"]["ast"]) as boolean && + (typedObj["columns"] === null || + Array.isArray(typedObj["columns"]) && + typedObj["columns"].every((e: any) => + isColumnRefItem(e) as boolean + )) + ) +} + +export function isParseOptions(obj: unknown): obj is ParseOptions { + const typedObj = obj as ParseOptions + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["includeLocations"] === "undefined" || + typedObj["includeLocations"] === false || + typedObj["includeLocations"] === true) + ) +} + +export function isOption(obj: unknown): obj is Option { + const typedObj = obj as Option + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["database"] === "undefined" || + typeof typedObj["database"] === "string") && + (typeof typedObj["type"] === "undefined" || + typeof typedObj["type"] === "string") && + (typeof typedObj["trimQuery"] === "undefined" || + typedObj["trimQuery"] === false || + typedObj["trimQuery"] === true) && + (typeof typedObj["parseOptions"] === "undefined" || + isParseOptions(typedObj["parseOptions"]) as boolean) + ) +} + +export function isTableColumnAst(obj: unknown): obj is TableColumnAst { + const typedObj = obj as TableColumnAst + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + Array.isArray(typedObj["tableList"]) && + typedObj["tableList"].every((e: any) => + typeof e === "string" + ) && + Array.isArray(typedObj["columnList"]) && + typedObj["columnList"].every((e: any) => + typeof e === "string" + ) && + (isSelect(typedObj["ast"]) as boolean || + isInsert_Replace(typedObj["ast"]) as boolean || + isUpdate(typedObj["ast"]) as boolean || + isDelete(typedObj["ast"]) as boolean || + isAlter(typedObj["ast"]) as boolean || + isUse(typedObj["ast"]) as boolean || + isCreateTable(typedObj["ast"]) as boolean || + isCreateDatabase(typedObj["ast"]) as boolean || + isCreateSchema(typedObj["ast"]) as boolean || + isCreateIndex(typedObj["ast"]) as boolean || + isCreateView(typedObj["ast"]) as boolean || + isCreateTrigger(typedObj["ast"]) as boolean || + isCreateUser(typedObj["ast"]) as boolean || + isDropTable(typedObj["ast"]) as boolean || + isDropDatabase(typedObj["ast"]) as boolean || + isDropView(typedObj["ast"]) as boolean || + isDropIndex(typedObj["ast"]) as boolean || + isDropTrigger(typedObj["ast"]) as boolean || + isShow(typedObj["ast"]) as boolean || + isDesc(typedObj["ast"]) as boolean || + isExplain(typedObj["ast"]) as boolean || + isCall(typedObj["ast"]) as boolean || + isSet(typedObj["ast"]) as boolean || + isLock(typedObj["ast"]) as boolean || + isUnlock(typedObj["ast"]) as boolean || + isGrant(typedObj["ast"]) as boolean || + isLoadData(typedObj["ast"]) as boolean || + isTruncate(typedObj["ast"]) as boolean || + isRename(typedObj["ast"]) as boolean || + isTransaction(typedObj["ast"]) as boolean || + Array.isArray(typedObj["ast"]) && + typedObj["ast"].every((e: any) => + isAST(e) as boolean + )) && + (typeof typedObj["parentheses"] === "undefined" || + typedObj["parentheses"] === false || + typedObj["parentheses"] === true) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isBaseFrom(obj: unknown): obj is BaseFrom { + const typedObj = obj as BaseFrom + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["db"] === null || + typeof typedObj["db"] === "string") && + typeof typedObj["table"] === "string" && + (typedObj["as"] === null || + typeof typedObj["as"] === "string") && + (typeof typedObj["schema"] === "undefined" || + typeof typedObj["schema"] === "string") && + (typeof typedObj["addition"] === "undefined" || + typedObj["addition"] === false || + typedObj["addition"] === true) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isJoin(obj: unknown): obj is Join { + const typedObj = obj as Join + return ( + isBaseFrom(typedObj) as boolean && + (typedObj["join"] === "INNER JOIN" || + typedObj["join"] === "LEFT JOIN" || + typedObj["join"] === "RIGHT JOIN") && + (typeof typedObj["using"] === "undefined" || + Array.isArray(typedObj["using"]) && + typedObj["using"].every((e: any) => + typeof e === "string" + )) && + (typeof typedObj["on"] === "undefined" || + isBinary(typedObj["on"]) as boolean) + ) +} + +export function isTableExpr(obj: unknown): obj is TableExpr { + const typedObj = obj as TableExpr + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["expr"] !== null && + typeof typedObj["expr"] === "object" || + typeof typedObj["expr"] === "function") && + Array.isArray(typedObj["expr"]["tableList"]) && + typedObj["expr"]["tableList"].every((e: any) => + typeof e === "string" + ) && + Array.isArray(typedObj["expr"]["columnList"]) && + typedObj["expr"]["columnList"].every((e: any) => + typeof e === "string" + ) && + isSelect(typedObj["expr"]["ast"]) as boolean && + typeof typedObj["expr"]["parentheses"] === "boolean" && + (typedObj["as"] === null || + typeof typedObj["as"] === "string") + ) +} + +export function isDual(obj: unknown): obj is Dual { + const typedObj = obj as Dual + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "dual" && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isFrom(obj: unknown): obj is From { + const typedObj = obj as From + return ( + (isBaseFrom(typedObj) as boolean || + isJoin(typedObj) as boolean || + isTableExpr(typedObj) as boolean || + isDual(typedObj) as boolean) + ) +} + +export function isLimitValue(obj: unknown): obj is LimitValue { + const typedObj = obj as LimitValue + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typeof typedObj["type"] === "string" && + typeof typedObj["value"] === "number" && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isLimit(obj: unknown): obj is Limit { + const typedObj = obj as Limit + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typeof typedObj["seperator"] === "string" && + Array.isArray(typedObj["value"]) && + typedObj["value"].every((e: any) => + isLimitValue(e) as boolean + ) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isOrderBy(obj: unknown): obj is OrderBy { + const typedObj = obj as OrderBy + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["type"] === null || + typedObj["type"] === "ASC" || + typedObj["type"] === "DESC") && + isExpressionValue(typedObj["expr"]) as boolean && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isValueExpr(obj: unknown): obj is ValueExpr { + const typedObj = obj as ValueExpr + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["type"] === "string" || + typedObj["type"] === "number" || + typedObj["type"] === "boolean" || + typedObj["type"] === "backticks_quote_string" || + typedObj["type"] === "hex_string" || + typedObj["type"] === "full_hex_string" || + typedObj["type"] === "natural_string" || + typedObj["type"] === "bit_string" || + typedObj["type"] === "double_quote_string" || + typedObj["type"] === "single_quote_string" || + typedObj["type"] === "bool" || + typedObj["type"] === "null" || + typedObj["type"] === "star" || + typedObj["type"] === "param" || + typedObj["type"] === "origin" || + typedObj["type"] === "date" || + typedObj["type"] === "datetime" || + typedObj["type"] === "default" || + typedObj["type"] === "time" || + typedObj["type"] === "timestamp") && + typeof typedObj["value"] === "T" + ) +} + +export function isSortDirection(obj: unknown): obj is SortDirection { + const typedObj = obj as SortDirection + return ( + (typedObj === "ASC" || + typedObj === "DESC" || + typedObj === "asc" || + typedObj === "desc") + ) +} + +export function isColumnRefItem(obj: unknown): obj is ColumnRefItem { + const typedObj = obj as ColumnRefItem + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "column_ref" && + (typeof typedObj["table"] === "undefined" || + typedObj["table"] === null || + typeof typedObj["table"] === "string") && + (typeof typedObj["column"] === "string" || + (typedObj["column"] !== null && + typeof typedObj["column"] === "object" || + typeof typedObj["column"] === "function") && + (typedObj["column"]["expr"] !== null && + typeof typedObj["column"]["expr"] === "object" || + typeof typedObj["column"]["expr"] === "function") && + (typedObj["column"]["expr"]["type"] === "string" || + typedObj["column"]["expr"]["type"] === "number" || + typedObj["column"]["expr"]["type"] === "boolean" || + typedObj["column"]["expr"]["type"] === "backticks_quote_string" || + typedObj["column"]["expr"]["type"] === "hex_string" || + typedObj["column"]["expr"]["type"] === "full_hex_string" || + typedObj["column"]["expr"]["type"] === "natural_string" || + typedObj["column"]["expr"]["type"] === "bit_string" || + typedObj["column"]["expr"]["type"] === "double_quote_string" || + typedObj["column"]["expr"]["type"] === "single_quote_string" || + typedObj["column"]["expr"]["type"] === "bool" || + typedObj["column"]["expr"]["type"] === "null" || + typedObj["column"]["expr"]["type"] === "star" || + typedObj["column"]["expr"]["type"] === "param" || + typedObj["column"]["expr"]["type"] === "origin" || + typedObj["column"]["expr"]["type"] === "date" || + typedObj["column"]["expr"]["type"] === "datetime" || + typedObj["column"]["expr"]["type"] === "default" || + typedObj["column"]["expr"]["type"] === "time" || + typedObj["column"]["expr"]["type"] === "timestamp") && + (typeof typedObj["column"]["expr"]["value"] === "string" || + typeof typedObj["column"]["expr"]["value"] === "number" || + typedObj["column"]["expr"]["value"] === false || + typedObj["column"]["expr"]["value"] === true)) && + (typeof typedObj["options"] === "undefined" || + isExprList(typedObj["options"]) as boolean) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") && + (typeof typedObj["collate"] === "undefined" || + typedObj["collate"] === null || + isCollateExpr(typedObj["collate"]) as boolean) && + (typeof typedObj["order_by"] === "undefined" || + typedObj["order_by"] === null || + typedObj["order_by"] === "ASC" || + typedObj["order_by"] === "DESC" || + typedObj["order_by"] === "asc" || + typedObj["order_by"] === "desc") + ) +} + +export function isColumnRef(obj: unknown): obj is ColumnRef { + const typedObj = obj as ColumnRef + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "column_ref" && + (typeof typedObj["table"] === "undefined" || + typedObj["table"] === null || + typeof typedObj["table"] === "string") && + (typeof typedObj["column"] === "string" || + (typedObj["column"] !== null && + typeof typedObj["column"] === "object" || + typeof typedObj["column"] === "function") && + (typedObj["column"]["expr"] !== null && + typeof typedObj["column"]["expr"] === "object" || + typeof typedObj["column"]["expr"] === "function") && + (typedObj["column"]["expr"]["type"] === "string" || + typedObj["column"]["expr"]["type"] === "number" || + typedObj["column"]["expr"]["type"] === "boolean" || + typedObj["column"]["expr"]["type"] === "backticks_quote_string" || + typedObj["column"]["expr"]["type"] === "hex_string" || + typedObj["column"]["expr"]["type"] === "full_hex_string" || + typedObj["column"]["expr"]["type"] === "natural_string" || + typedObj["column"]["expr"]["type"] === "bit_string" || + typedObj["column"]["expr"]["type"] === "double_quote_string" || + typedObj["column"]["expr"]["type"] === "single_quote_string" || + typedObj["column"]["expr"]["type"] === "bool" || + typedObj["column"]["expr"]["type"] === "null" || + typedObj["column"]["expr"]["type"] === "star" || + typedObj["column"]["expr"]["type"] === "param" || + typedObj["column"]["expr"]["type"] === "origin" || + typedObj["column"]["expr"]["type"] === "date" || + typedObj["column"]["expr"]["type"] === "datetime" || + typedObj["column"]["expr"]["type"] === "default" || + typedObj["column"]["expr"]["type"] === "time" || + typedObj["column"]["expr"]["type"] === "timestamp") && + (typeof typedObj["column"]["expr"]["value"] === "string" || + typeof typedObj["column"]["expr"]["value"] === "number" || + typedObj["column"]["expr"]["value"] === false || + typedObj["column"]["expr"]["value"] === true)) && + (typeof typedObj["options"] === "undefined" || + isExprList(typedObj["options"]) as boolean) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") && + (typeof typedObj["collate"] === "undefined" || + typedObj["collate"] === null || + isCollateExpr(typedObj["collate"]) as boolean) && + (typeof typedObj["order_by"] === "undefined" || + typedObj["order_by"] === null || + typedObj["order_by"] === "ASC" || + typedObj["order_by"] === "DESC" || + typedObj["order_by"] === "asc" || + typedObj["order_by"] === "desc") + ) +} + +export function isSetList(obj: unknown): obj is SetList { + const typedObj = obj as SetList + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typeof typedObj["column"] === "string" && + isExpressionValue(typedObj["value"]) as boolean && + (typedObj["table"] === null || + typeof typedObj["table"] === "string") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isInsertReplaceValue(obj: unknown): obj is InsertReplaceValue { + const typedObj = obj as InsertReplaceValue + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "expr_list" && + Array.isArray(typedObj["value"]) && + typedObj["value"].every((e: any) => + isExpressionValue(e) as boolean + ) && + (typedObj["prefix"] === null || + typeof typedObj["prefix"] === "string") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isStar(obj: unknown): obj is Star { + const typedObj = obj as Star + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "star" && + (typedObj["value"] === "" || + typedObj["value"] === "*") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isCase(obj: unknown): obj is Case { + const typedObj = obj as Case + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "case" && + typedObj["expr"] === null && + Array.isArray(typedObj["args"]) && + typedObj["args"].every((e: any) => + ((e !== null && + typeof e === "object" || + typeof e === "function") && + isBinary(e["cond"]) as boolean && + isExpressionValue(e["result"]) as boolean && + e["type"] === "when" || + (e !== null && + typeof e === "object" || + typeof e === "function") && + isExpressionValue(e["result"]) as boolean && + e["type"] === "else") + ) + ) +} + +export function isCast(obj: unknown): obj is Cast { + const typedObj = obj as Cast + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "cast" && + typedObj["keyword"] === "cast" && + isExpressionValue(typedObj["expr"]) as boolean && + typedObj["symbol"] === "as" && + Array.isArray(typedObj["target"]) && + typedObj["target"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["dataType"] === "string" && + (typeof e["quoted"] === "undefined" || + typeof e["quoted"] === "string") + ) + ) +} + +export function isAggrFunc(obj: unknown): obj is AggrFunc { + const typedObj = obj as AggrFunc + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "aggr_func" && + typeof typedObj["name"] === "string" && + (typedObj["args"] !== null && + typeof typedObj["args"] === "object" || + typeof typedObj["args"] === "function") && + isExpressionValue(typedObj["args"]["expr"]) as boolean && + (typeof typedObj["args"]["distinct"] === "undefined" || + typedObj["args"]["distinct"] === null || + typedObj["args"]["distinct"] === "DISTINCT") && + (typeof typedObj["args"]["orderby"] === "undefined" || + typedObj["args"]["orderby"] === null || + Array.isArray(typedObj["args"]["orderby"]) && + typedObj["args"]["orderby"].every((e: any) => + isOrderBy(e) as boolean + )) && + (typeof typedObj["args"]["parentheses"] === "undefined" || + typedObj["args"]["parentheses"] === false || + typedObj["args"]["parentheses"] === true) && + (typeof typedObj["args"]["separator"] === "undefined" || + typedObj["args"]["separator"] === null || + typeof typedObj["args"]["separator"] === "string" || + (typedObj["args"]["separator"] !== null && + typeof typedObj["args"]["separator"] === "object" || + typeof typedObj["args"]["separator"] === "function") && + typeof typedObj["args"]["separator"]["keyword"] === "string" && + isValue(typedObj["args"]["separator"]["value"]) as boolean) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") && + (typedObj["over"] === null || + (typedObj["over"] !== null && + typeof typedObj["over"] === "object" || + typeof typedObj["over"] === "function") && + typedObj["over"]["type"] === "window" && + isAsWindowSpec(typedObj["over"]["as_window_specification"]) as boolean) + ) +} + +export function isFunctionName(obj: unknown): obj is FunctionName { + const typedObj = obj as FunctionName + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["schema"] === "undefined" || + (typedObj["schema"] !== null && + typeof typedObj["schema"] === "object" || + typeof typedObj["schema"] === "function") && + typeof typedObj["schema"]["value"] === "string" && + typeof typedObj["schema"]["type"] === "string") && + Array.isArray(typedObj["name"]) && + typedObj["name"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["type"] === "string" || + e["type"] === "number" || + e["type"] === "boolean" || + e["type"] === "backticks_quote_string" || + e["type"] === "hex_string" || + e["type"] === "full_hex_string" || + e["type"] === "natural_string" || + e["type"] === "bit_string" || + e["type"] === "double_quote_string" || + e["type"] === "single_quote_string" || + e["type"] === "bool" || + e["type"] === "null" || + e["type"] === "star" || + e["type"] === "param" || + e["type"] === "origin" || + e["type"] === "date" || + e["type"] === "datetime" || + e["type"] === "default" || + e["type"] === "time" || + e["type"] === "timestamp") && + typeof e["value"] === "string" + ) + ) +} + +export function isFunction(obj: unknown): obj is Function { + const typedObj = obj as Function + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "function" && + isFunctionName(typedObj["name"]) as boolean && + (typeof typedObj["args"] === "undefined" || + isExprList(typedObj["args"]) as boolean) && + (typeof typedObj["suffix"] === "undefined" || + typedObj["suffix"] === null || + isOnUpdateCurrentTimestamp(typedObj["suffix"]) as boolean) && + (typeof typedObj["over"] === "undefined" || + typedObj["over"] === null || + (typedObj["over"] !== null && + typeof typedObj["over"] === "object" || + typeof typedObj["over"] === "function") && + typedObj["over"]["type"] === "window" && + isAsWindowSpec(typedObj["over"]["as_window_specification"]) as boolean) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isColumn(obj: unknown): obj is Column { + const typedObj = obj as Column + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + isExpressionValue(typedObj["expr"]) as boolean && + (typedObj["as"] === null || + typeof typedObj["as"] === "string" || + (typedObj["as"] !== null && + typeof typedObj["as"] === "object" || + typeof typedObj["as"] === "function") && + (typedObj["as"]["type"] === "string" || + typedObj["as"]["type"] === "number" || + typedObj["as"]["type"] === "boolean" || + typedObj["as"]["type"] === "backticks_quote_string" || + typedObj["as"]["type"] === "hex_string" || + typedObj["as"]["type"] === "full_hex_string" || + typedObj["as"]["type"] === "natural_string" || + typedObj["as"]["type"] === "bit_string" || + typedObj["as"]["type"] === "double_quote_string" || + typedObj["as"]["type"] === "single_quote_string" || + typedObj["as"]["type"] === "bool" || + typedObj["as"]["type"] === "null" || + typedObj["as"]["type"] === "star" || + typedObj["as"]["type"] === "param" || + typedObj["as"]["type"] === "origin" || + typedObj["as"]["type"] === "date" || + typedObj["as"]["type"] === "datetime" || + typedObj["as"]["type"] === "default" || + typedObj["as"]["type"] === "time" || + typedObj["as"]["type"] === "timestamp") && + typeof typedObj["as"]["value"] === "string") && + (typeof typedObj["type"] === "undefined" || + typeof typedObj["type"] === "string") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isInterval(obj: unknown): obj is Interval { + const typedObj = obj as Interval + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "interval" && + typeof typedObj["unit"] === "string" && + (typedObj["expr"] !== null && + typeof typedObj["expr"] === "object" || + typeof typedObj["expr"] === "function") && + (typedObj["expr"]["type"] === "string" || + typedObj["expr"]["type"] === "number" || + typedObj["expr"]["type"] === "boolean" || + typedObj["expr"]["type"] === "backticks_quote_string" || + typedObj["expr"]["type"] === "hex_string" || + typedObj["expr"]["type"] === "full_hex_string" || + typedObj["expr"]["type"] === "natural_string" || + typedObj["expr"]["type"] === "bit_string" || + typedObj["expr"]["type"] === "double_quote_string" || + typedObj["expr"]["type"] === "single_quote_string" || + typedObj["expr"]["type"] === "bool" || + typedObj["expr"]["type"] === "null" || + typedObj["expr"]["type"] === "star" || + typedObj["expr"]["type"] === "param" || + typedObj["expr"]["type"] === "origin" || + typedObj["expr"]["type"] === "date" || + typedObj["expr"]["type"] === "datetime" || + typedObj["expr"]["type"] === "default" || + typedObj["expr"]["type"] === "time" || + typedObj["expr"]["type"] === "timestamp") && + (typeof typedObj["expr"]["value"] === "string" || + typeof typedObj["expr"]["value"] === "number" || + typedObj["expr"]["value"] === false || + typedObj["expr"]["value"] === true) && + (typedObj["expr"] !== null && + typeof typedObj["expr"] === "object" || + typeof typedObj["expr"] === "function") && + (typeof typedObj["expr"]["loc"] === "undefined" || + (typedObj["expr"]["loc"] !== null && + typeof typedObj["expr"]["loc"] === "object" || + typeof typedObj["expr"]["loc"] === "function") && + (typedObj["expr"]["loc"]["start"] !== null && + typeof typedObj["expr"]["loc"]["start"] === "object" || + typeof typedObj["expr"]["loc"]["start"] === "function") && + typeof typedObj["expr"]["loc"]["start"]["line"] === "number" && + typeof typedObj["expr"]["loc"]["start"]["column"] === "number" && + typeof typedObj["expr"]["loc"]["start"]["offset"] === "number" && + (typedObj["expr"]["loc"]["end"] !== null && + typeof typedObj["expr"]["loc"]["end"] === "object" || + typeof typedObj["expr"]["loc"]["end"] === "function") && + typeof typedObj["expr"]["loc"]["end"]["line"] === "number" && + typeof typedObj["expr"]["loc"]["end"]["column"] === "number" && + typeof typedObj["expr"]["loc"]["end"]["offset"] === "number") + ) +} + +export function isParam(obj: unknown): obj is Param { + const typedObj = obj as Param + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "param" && + typeof typedObj["value"] === "string" && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isVar(obj: unknown): obj is Var { + const typedObj = obj as Var + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "var" && + typeof typedObj["name"] === "string" && + Array.isArray(typedObj["members"]) && + typedObj["members"].every((e: any) => + typeof e === "string" + ) && + typeof typedObj["prefix"] === "string" && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isValue(obj: unknown): obj is Value { + const typedObj = obj as Value + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typeof typedObj["type"] === "string" && + (typedObj["value"] === null || + typeof typedObj["value"] === "string" || + typeof typedObj["value"] === "number" || + typedObj["value"] === false || + typedObj["value"] === true) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isBinary(obj: unknown): obj is Binary { + const typedObj = obj as Binary + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "binary_expr" && + typeof typedObj["operator"] === "string" && + (isTableColumnAst(typedObj["left"]) as boolean || + isColumnRefItem(typedObj["left"]) as boolean || + isStar(typedObj["left"]) as boolean || + isCase(typedObj["left"]) as boolean || + isCast(typedObj["left"]) as boolean || + isAggrFunc(typedObj["left"]) as boolean || + isFunction(typedObj["left"]) as boolean || + isInterval(typedObj["left"]) as boolean || + isParam(typedObj["left"]) as boolean || + isVar(typedObj["left"]) as boolean || + isValue(typedObj["left"]) as boolean || + isBinary(typedObj["left"]) as boolean || + isUnary(typedObj["left"]) as boolean || + isExprList(typedObj["left"]) as boolean) && + (isTableColumnAst(typedObj["right"]) as boolean || + isColumnRefItem(typedObj["right"]) as boolean || + isStar(typedObj["right"]) as boolean || + isCase(typedObj["right"]) as boolean || + isCast(typedObj["right"]) as boolean || + isAggrFunc(typedObj["right"]) as boolean || + isFunction(typedObj["right"]) as boolean || + isInterval(typedObj["right"]) as boolean || + isParam(typedObj["right"]) as boolean || + isVar(typedObj["right"]) as boolean || + isValue(typedObj["right"]) as boolean || + isBinary(typedObj["right"]) as boolean || + isUnary(typedObj["right"]) as boolean || + isExprList(typedObj["right"]) as boolean) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") && + (typeof typedObj["parentheses"] === "undefined" || + typedObj["parentheses"] === false || + typedObj["parentheses"] === true) + ) +} + +export function isUnary(obj: unknown): obj is Unary { + const typedObj = obj as Unary + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "unary_expr" && + typeof typedObj["operator"] === "string" && + isExpressionValue(typedObj["expr"]) as boolean && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") && + (typeof typedObj["parentheses"] === "undefined" || + typedObj["parentheses"] === false || + typedObj["parentheses"] === true) + ) +} + +export function isExpr(obj: unknown): obj is Expr { + const typedObj = obj as Expr + return ( + (isBinary(typedObj) as boolean || + isUnary(typedObj) as boolean) + ) +} + +export function isExpressionValue(obj: unknown): obj is ExpressionValue { + const typedObj = obj as ExpressionValue + return ( + (isTableColumnAst(typedObj) as boolean || + isColumnRefItem(typedObj) as boolean || + isStar(typedObj) as boolean || + isCase(typedObj) as boolean || + isCast(typedObj) as boolean || + isAggrFunc(typedObj) as boolean || + isFunction(typedObj) as boolean || + isInterval(typedObj) as boolean || + isParam(typedObj) as boolean || + isVar(typedObj) as boolean || + isValue(typedObj) as boolean || + isBinary(typedObj) as boolean || + isUnary(typedObj) as boolean) + ) +} + +export function isExprList(obj: unknown): obj is ExprList { + const typedObj = obj as ExprList + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "expr_list" && + (typedObj["value"] === null || + Array.isArray(typedObj["value"]) && + typedObj["value"].every((e: any) => + isExpressionValue(e) as boolean + )) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") && + (typeof typedObj["parentheses"] === "undefined" || + typedObj["parentheses"] === false || + typedObj["parentheses"] === true) && + (typeof typedObj["separator"] === "undefined" || + typeof typedObj["separator"] === "string") + ) +} + +export function isPartitionBy(obj: unknown): obj is PartitionBy { + const typedObj = obj as PartitionBy + return ( + Array.isArray(typedObj) && + typedObj.every((e: any) => + isColumn(e) as boolean + ) + ) +} + +export function isWindowSpec(obj: unknown): obj is WindowSpec { + const typedObj = obj as WindowSpec + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["name"] === null || + typeof typedObj["name"] === "string") && + (typedObj["partitionby"] === null || + isPartitionBy(typedObj["partitionby"]) as boolean) && + (typedObj["orderby"] === null || + Array.isArray(typedObj["orderby"]) && + typedObj["orderby"].every((e: any) => + isOrderBy(e) as boolean + )) && + (typedObj["window_frame_clause"] === null || + isBinary(typedObj["window_frame_clause"]) as boolean) + ) +} + +export function isWindowFrameClause(obj: unknown): obj is WindowFrameClause { + const typedObj = obj as WindowFrameClause + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "binary_expr" && + typeof typedObj["operator"] === "string" && + (isTableColumnAst(typedObj["left"]) as boolean || + isColumnRefItem(typedObj["left"]) as boolean || + isStar(typedObj["left"]) as boolean || + isCase(typedObj["left"]) as boolean || + isCast(typedObj["left"]) as boolean || + isAggrFunc(typedObj["left"]) as boolean || + isFunction(typedObj["left"]) as boolean || + isInterval(typedObj["left"]) as boolean || + isParam(typedObj["left"]) as boolean || + isVar(typedObj["left"]) as boolean || + isValue(typedObj["left"]) as boolean || + isBinary(typedObj["left"]) as boolean || + isUnary(typedObj["left"]) as boolean || + isExprList(typedObj["left"]) as boolean) && + (isTableColumnAst(typedObj["right"]) as boolean || + isColumnRefItem(typedObj["right"]) as boolean || + isStar(typedObj["right"]) as boolean || + isCase(typedObj["right"]) as boolean || + isCast(typedObj["right"]) as boolean || + isAggrFunc(typedObj["right"]) as boolean || + isFunction(typedObj["right"]) as boolean || + isInterval(typedObj["right"]) as boolean || + isParam(typedObj["right"]) as boolean || + isVar(typedObj["right"]) as boolean || + isValue(typedObj["right"]) as boolean || + isBinary(typedObj["right"]) as boolean || + isUnary(typedObj["right"]) as boolean || + isExprList(typedObj["right"]) as boolean) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") && + (typeof typedObj["parentheses"] === "undefined" || + typedObj["parentheses"] === false || + typedObj["parentheses"] === true) + ) +} + +export function isAsWindowSpec(obj: unknown): obj is AsWindowSpec { + const typedObj = obj as AsWindowSpec + return ( + (typeof typedObj === "string" || + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + isWindowSpec(typedObj["window_specification"]) as boolean && + typeof typedObj["parentheses"] === "boolean") + ) +} + +export function isNamedWindowExpr(obj: unknown): obj is NamedWindowExpr { + const typedObj = obj as NamedWindowExpr + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typeof typedObj["name"] === "string" && + isAsWindowSpec(typedObj["as_window_specification"]) as boolean + ) +} + +export function isWindowExpr(obj: unknown): obj is WindowExpr { + const typedObj = obj as WindowExpr + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["keyword"] === "window" && + typedObj["type"] === "window" && + Array.isArray(typedObj["expr"]) && + typedObj["expr"].every((e: any) => + isNamedWindowExpr(e) as boolean + ) + ) +} + +export function isSelect(obj: unknown): obj is Select { + const typedObj = obj as Select + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["with"] === null || + Array.isArray(typedObj["with"]) && + typedObj["with"].every((e: any) => + isWith(e) as boolean + )) && + typedObj["type"] === "select" && + (typedObj["options"] === null || + Array.isArray(typedObj["options"]) && + typedObj["options"].every((e: any) => + (e === "SQL_CALC_FOUND_ROWS" || + e === "SQL_CACHE" || + e === "SQL_NO_CACHE" || + e === "SQL_SMALL_RESULT" || + e === "SQL_BIG_RESULT" || + e === "SQL_BUFFER_RESULT") + )) && + (typedObj["distinct"] === null || + typedObj["distinct"] === "DISTINCT") && + Array.isArray(typedObj["columns"]) && + typedObj["columns"].every((e: any) => + isColumn(e) as boolean + ) && + (typedObj["into"] !== null && + typeof typedObj["into"] === "object" || + typeof typedObj["into"] === "function") && + (typeof typedObj["into"]["keyword"] === "undefined" || + typeof typedObj["into"]["keyword"] === "string") && + (typeof typedObj["into"]["type"] === "undefined" || + typeof typedObj["into"]["type"] === "string") && + (typeof typedObj["into"]["expr"] === "undefined" || + isValue(typedObj["into"]["expr"]) as boolean || + Array.isArray(typedObj["into"]["expr"]) && + typedObj["into"]["expr"].every((e: any) => + isVar(e) as boolean + )) && + (typedObj["into"]["position"] === null || + typedObj["into"]["position"] === "column" || + typedObj["into"]["position"] === "from" || + typedObj["into"]["position"] === "end") && + (typedObj["from"] === null || + isTableExpr(typedObj["from"]) as boolean || + Array.isArray(typedObj["from"]) && + typedObj["from"].every((e: any) => + isFrom(e) as boolean + ) || + (typedObj["from"] !== null && + typeof typedObj["from"] === "object" || + typeof typedObj["from"] === "function") && + Array.isArray(typedObj["from"]["expr"]) && + typedObj["from"]["expr"].every((e: any) => + isFrom(e) as boolean + ) && + (typedObj["from"]["parentheses"] !== null && + typeof typedObj["from"]["parentheses"] === "object" || + typeof typedObj["from"]["parentheses"] === "function") && + typeof typedObj["from"]["parentheses"]["length"] === "number" && + Array.isArray(typedObj["from"]["joins"]) && + typedObj["from"]["joins"].every((e: any) => + isFrom(e) as boolean + )) && + (typedObj["where"] === null || + isFunction(typedObj["where"]) as boolean || + isBinary(typedObj["where"]) as boolean || + isUnary(typedObj["where"]) as boolean) && + (typedObj["groupby"] === null || + (typedObj["groupby"] !== null && + typeof typedObj["groupby"] === "object" || + typeof typedObj["groupby"] === "function") && + (typedObj["groupby"]["columns"] === null || + Array.isArray(typedObj["groupby"]["columns"]) && + typedObj["groupby"]["columns"].every((e: any) => + isColumnRefItem(e) as boolean + )) && + Array.isArray(typedObj["groupby"]["modifiers"]) && + typedObj["groupby"]["modifiers"].every((e: any) => + (e === null || + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["type"] === "string" || + e["type"] === "number" || + e["type"] === "boolean" || + e["type"] === "backticks_quote_string" || + e["type"] === "hex_string" || + e["type"] === "full_hex_string" || + e["type"] === "natural_string" || + e["type"] === "bit_string" || + e["type"] === "double_quote_string" || + e["type"] === "single_quote_string" || + e["type"] === "bool" || + e["type"] === "null" || + e["type"] === "star" || + e["type"] === "param" || + e["type"] === "origin" || + e["type"] === "date" || + e["type"] === "datetime" || + e["type"] === "default" || + e["type"] === "time" || + e["type"] === "timestamp") && + typeof e["value"] === "string") + )) && + (typedObj["having"] === null || + isBinary(typedObj["having"]) as boolean) && + (typedObj["orderby"] === null || + Array.isArray(typedObj["orderby"]) && + typedObj["orderby"].every((e: any) => + isOrderBy(e) as boolean + )) && + (typedObj["limit"] === null || + isLimit(typedObj["limit"]) as boolean) && + (typedObj["window"] === null || + isWindowExpr(typedObj["window"]) as boolean) && + (typeof typedObj["_orderby"] === "undefined" || + typedObj["_orderby"] === null || + Array.isArray(typedObj["_orderby"]) && + typedObj["_orderby"].every((e: any) => + isOrderBy(e) as boolean + )) && + (typeof typedObj["_limit"] === "undefined" || + typedObj["_limit"] === null || + isLimit(typedObj["_limit"]) as boolean) && + (typeof typedObj["parentheses_symbol"] === "undefined" || + typedObj["parentheses_symbol"] === false || + typedObj["parentheses_symbol"] === true) && + (typeof typedObj["_parentheses"] === "undefined" || + typedObj["_parentheses"] === false || + typedObj["_parentheses"] === true) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") && + (typeof typedObj["_next"] === "undefined" || + isSelect(typedObj["_next"]) as boolean) && + (typeof typedObj["set_op"] === "undefined" || + typeof typedObj["set_op"] === "string") && + (typedObj["collate"] === null || + isCollateExpr(typedObj["collate"]) as boolean) && + (typedObj["locking_read"] === null || + typeof typedObj["locking_read"] === "string") + ) +} + +export function isInsert_Replace(obj: unknown): obj is Insert_Replace { + const typedObj = obj as Insert_Replace + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["type"] === "replace" || + typedObj["type"] === "insert") && + (isBaseFrom(typedObj["table"]) as boolean || + isJoin(typedObj["table"]) as boolean || + isTableExpr(typedObj["table"]) as boolean || + isDual(typedObj["table"]) as boolean || + Array.isArray(typedObj["table"]) && + typedObj["table"].every((e: any) => + isFrom(e) as boolean + )) && + (typedObj["columns"] === null || + Array.isArray(typedObj["columns"]) && + typedObj["columns"].every((e: any) => + typeof e === "string" + )) && + (typeof typedObj["values"] === "undefined" || + isSelect(typedObj["values"]) as boolean || + (typedObj["values"] !== null && + typeof typedObj["values"] === "object" || + typeof typedObj["values"] === "function") && + typedObj["values"]["type"] === "values" && + Array.isArray(typedObj["values"]["values"]) && + typedObj["values"]["values"].every((e: any) => + isInsertReplaceValue(e) as boolean + )) && + (typeof typedObj["set"] === "undefined" || + Array.isArray(typedObj["set"]) && + typedObj["set"].every((e: any) => + isSetList(e) as boolean + )) && + (typedObj["partition"] === null || + Array.isArray(typedObj["partition"]) && + typedObj["partition"].every((e: any) => + typeof e === "string" + )) && + (typedObj["prefix"] === "" || + typedObj["prefix"] === "ignore" || + typedObj["prefix"] === "into" || + typedObj["prefix"] === "ignore into") && + (typedObj["on_duplicate_update"] === null || + (typedObj["on_duplicate_update"] !== null && + typeof typedObj["on_duplicate_update"] === "object" || + typeof typedObj["on_duplicate_update"] === "function") && + typedObj["on_duplicate_update"]["keyword"] === "on duplicate key update" && + Array.isArray(typedObj["on_duplicate_update"]["set"]) && + typedObj["on_duplicate_update"]["set"].every((e: any) => + isSetList(e) as boolean + )) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isUpdate(obj: unknown): obj is Update { + const typedObj = obj as Update + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["with"] === null || + Array.isArray(typedObj["with"]) && + typedObj["with"].every((e: any) => + isWith(e) as boolean + )) && + typedObj["type"] === "update" && + (typedObj["table"] === null || + Array.isArray(typedObj["table"]) && + typedObj["table"].every((e: any) => + isFrom(e) as boolean + )) && + Array.isArray(typedObj["set"]) && + typedObj["set"].every((e: any) => + isSetList(e) as boolean + ) && + (typedObj["where"] === null || + isFunction(typedObj["where"]) as boolean || + isBinary(typedObj["where"]) as boolean || + isUnary(typedObj["where"]) as boolean) && + (typedObj["orderby"] === null || + Array.isArray(typedObj["orderby"]) && + typedObj["orderby"].every((e: any) => + isOrderBy(e) as boolean + )) && + (typedObj["limit"] === null || + isLimit(typedObj["limit"]) as boolean) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isDelete(obj: unknown): obj is Delete { + const typedObj = obj as Delete + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["with"] === null || + Array.isArray(typedObj["with"]) && + typedObj["with"].every((e: any) => + isWith(e) as boolean + )) && + typedObj["type"] === "delete" && + (typedObj["table"] === null || + Array.isArray(typedObj["table"]) && + typedObj["table"].every((e: any) => + (isBaseFrom(e) as boolean && + (e !== null && + typeof e === "object" || + typeof e === "function") && + (typeof e["addition"] === "undefined" || + e["addition"] === false || + e["addition"] === true) || + isJoin(e) as boolean && + (e !== null && + typeof e === "object" || + typeof e === "function") && + (typeof e["addition"] === "undefined" || + e["addition"] === false || + e["addition"] === true) || + isTableExpr(e) as boolean && + (e !== null && + typeof e === "object" || + typeof e === "function") && + (typeof e["addition"] === "undefined" || + e["addition"] === false || + e["addition"] === true) || + isDual(e) as boolean && + (e !== null && + typeof e === "object" || + typeof e === "function") && + (typeof e["addition"] === "undefined" || + e["addition"] === false || + e["addition"] === true)) + )) && + Array.isArray(typedObj["from"]) && + typedObj["from"].every((e: any) => + isFrom(e) as boolean + ) && + (typedObj["where"] === null || + isFunction(typedObj["where"]) as boolean || + isBinary(typedObj["where"]) as boolean || + isUnary(typedObj["where"]) as boolean) && + (typedObj["orderby"] === null || + Array.isArray(typedObj["orderby"]) && + typedObj["orderby"].every((e: any) => + isOrderBy(e) as boolean + )) && + (typedObj["limit"] === null || + isLimit(typedObj["limit"]) as boolean) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isAlter(obj: unknown): obj is Alter { + const typedObj = obj as Alter + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + Array.isArray(typedObj["table"]) && + typedObj["table"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["db"] === null || + typeof e["db"] === "string") && + typeof e["table"] === "string" + ) && + Array.isArray(typedObj["expr"]) && + typedObj["expr"].every((e: any) => + isAlterExpr(e) as boolean + ) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isAlterExpr(obj: unknown): obj is AlterExpr { + const typedObj = obj as AlterExpr + return ( + (isAlterAddColumn(typedObj) as boolean || + isAlterDropColumn(typedObj) as boolean || + isAlterModifyColumn(typedObj) as boolean || + isAlterChangeColumn(typedObj) as boolean || + isAlterRenameTable(typedObj) as boolean || + isAlterRenameColumn(typedObj) as boolean || + isAlterAddIndex(typedObj) as boolean || + isAlterDropIndex(typedObj) as boolean || + isAlterDropKey(typedObj) as boolean || + isAlterAddConstraint(typedObj) as boolean || + isAlterDropConstraint(typedObj) as boolean || + isAlterAddPartition(typedObj) as boolean || + isAlterDropPartition(typedObj) as boolean || + isAlterAlgorithm(typedObj) as boolean || + isAlterLock(typedObj) as boolean || + isAlterTableOption(typedObj) as boolean) + ) +} + +export function isAlterAddColumn(obj: unknown): obj is AlterAddColumn { + const typedObj = obj as AlterAddColumn + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "column" && + typedObj["action"] === "add" && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === "COLUMN") && + isColumnRefItem(typedObj["column"]) as boolean && + isDataType(typedObj["definition"]) as boolean && + (typedObj["suffix"] === null || + typeof typedObj["suffix"] === "string" || + (typedObj["suffix"] !== null && + typeof typedObj["suffix"] === "object" || + typeof typedObj["suffix"] === "function") && + typeof typedObj["suffix"]["keyword"] === "string") + ) +} + +export function isAlterDropColumn(obj: unknown): obj is AlterDropColumn { + const typedObj = obj as AlterDropColumn + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "column" && + typedObj["action"] === "drop" && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === "COLUMN") && + isColumnRefItem(typedObj["column"]) as boolean + ) +} + +export function isAlterModifyColumn(obj: unknown): obj is AlterModifyColumn { + const typedObj = obj as AlterModifyColumn + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "column" && + typedObj["action"] === "modify" && + (typedObj["keyword"] === null || + typedObj["keyword"] === "COLUMN") && + isColumnRefItem(typedObj["column"]) as boolean && + isDataType(typedObj["definition"]) as boolean && + (typedObj["suffix"] === null || + typeof typedObj["suffix"] === "string" || + (typedObj["suffix"] !== null && + typeof typedObj["suffix"] === "object" || + typeof typedObj["suffix"] === "function") && + typeof typedObj["suffix"]["keyword"] === "string") + ) +} + +export function isAlterChangeColumn(obj: unknown): obj is AlterChangeColumn { + const typedObj = obj as AlterChangeColumn + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "column" && + typedObj["action"] === "change" && + (typedObj["keyword"] === null || + typedObj["keyword"] === "COLUMN") && + isColumnRefItem(typedObj["old_column"]) as boolean && + isColumnRefItem(typedObj["column"]) as boolean && + isDataType(typedObj["definition"]) as boolean && + (typedObj["suffix"] === null || + typeof typedObj["suffix"] === "string" || + (typedObj["suffix"] !== null && + typeof typedObj["suffix"] === "object" || + typeof typedObj["suffix"] === "function") && + typeof typedObj["suffix"]["keyword"] === "string") + ) +} + +export function isAlterRenameTable(obj: unknown): obj is AlterRenameTable { + const typedObj = obj as AlterRenameTable + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "table" && + typedObj["action"] === "rename" && + typeof typedObj["keyword"] === "string" && + typeof typedObj["table"] === "string" + ) +} + +export function isAlterRenameColumn(obj: unknown): obj is AlterRenameColumn { + const typedObj = obj as AlterRenameColumn + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "column" && + typedObj["action"] === "rename" && + typedObj["keyword"] === "column" && + isColumnRefItem(typedObj["old_column"]) as boolean && + typeof typedObj["prefix"] === "string" && + isColumnRefItem(typedObj["column"]) as boolean + ) +} + +export function isAlterAddIndex(obj: unknown): obj is AlterAddIndex { + const typedObj = obj as AlterAddIndex + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "index" && + typedObj["action"] === "add" && + typeof typedObj["keyword"] === "string" && + typeof typedObj["index"] === "string" && + Array.isArray(typedObj["definition"]) && + typedObj["definition"].every((e: any) => + isColumnRefItem(e) as boolean + ) && + (typedObj["index_type"] === null || + isIndexType(typedObj["index_type"]) as boolean) && + (typedObj["index_options"] === null || + Array.isArray(typedObj["index_options"]) && + typedObj["index_options"].every((e: any) => + isIndexOption(e) as boolean + )) + ) +} + +export function isAlterDropIndex(obj: unknown): obj is AlterDropIndex { + const typedObj = obj as AlterDropIndex + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "index" && + typedObj["action"] === "drop" && + typeof typedObj["keyword"] === "string" && + typeof typedObj["index"] === "string" + ) +} + +export function isAlterDropKey(obj: unknown): obj is AlterDropKey { + const typedObj = obj as AlterDropKey + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "key" && + typedObj["action"] === "drop" && + typeof typedObj["keyword"] === "string" && + typeof typedObj["key"] === "string" + ) +} + +export function isAlterAddConstraint(obj: unknown): obj is AlterAddConstraint { + const typedObj = obj as AlterAddConstraint + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "constraint" && + typedObj["action"] === "add" && + isCreateConstraintDefinition(typedObj["create_definitions"]) as boolean + ) +} + +export function isAlterDropConstraint(obj: unknown): obj is AlterDropConstraint { + const typedObj = obj as AlterDropConstraint + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "constraint" && + typedObj["action"] === "drop" && + typeof typedObj["keyword"] === "string" && + typeof typedObj["constraint"] === "string" + ) +} + +export function isAlterAddPartition(obj: unknown): obj is AlterAddPartition { + const typedObj = obj as AlterAddPartition + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "partition" && + typedObj["action"] === "add" && + typedObj["keyword"] === "PARTITION" && + Array.isArray(typedObj["partitions"]) && + typedObj["partitions"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["name"] !== null && + typeof e["name"] === "object" || + typeof e["name"] === "function") && + (e["name"]["type"] === "string" || + e["name"]["type"] === "number" || + e["name"]["type"] === "boolean" || + e["name"]["type"] === "backticks_quote_string" || + e["name"]["type"] === "hex_string" || + e["name"]["type"] === "full_hex_string" || + e["name"]["type"] === "natural_string" || + e["name"]["type"] === "bit_string" || + e["name"]["type"] === "double_quote_string" || + e["name"]["type"] === "single_quote_string" || + e["name"]["type"] === "bool" || + e["name"]["type"] === "null" || + e["name"]["type"] === "star" || + e["name"]["type"] === "param" || + e["name"]["type"] === "origin" || + e["name"]["type"] === "date" || + e["name"]["type"] === "datetime" || + e["name"]["type"] === "default" || + e["name"]["type"] === "time" || + e["name"]["type"] === "timestamp") && + (typeof e["name"]["value"] === "string" || + typeof e["name"]["value"] === "number" || + e["name"]["value"] === false || + e["name"]["value"] === true) && + (e["value"] !== null && + typeof e["value"] === "object" || + typeof e["value"] === "function") && + typeof e["value"]["type"] === "string" && + isValue(e["value"]["expr"]) as boolean && + typeof e["value"]["parentheses"] === "boolean" + ) + ) +} + +export function isAlterDropPartition(obj: unknown): obj is AlterDropPartition { + const typedObj = obj as AlterDropPartition + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "partition" && + typedObj["action"] === "drop" && + typedObj["keyword"] === "PARTITION" && + Array.isArray(typedObj["partitions"]) && + typedObj["partitions"].every((e: any) => + isColumn(e) as boolean + ) + ) +} + +export function isAlterAlgorithm(obj: unknown): obj is AlterAlgorithm { + const typedObj = obj as AlterAlgorithm + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "algorithm" && + typedObj["keyword"] === "algorithm" && + typeof typedObj["symbol"] === "string" && + typeof typedObj["algorithm"] === "string" + ) +} + +export function isAlterLock(obj: unknown): obj is AlterLock { + const typedObj = obj as AlterLock + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typedObj["resource"] === "lock" && + typedObj["keyword"] === "lock" && + typeof typedObj["symbol"] === "string" && + typeof typedObj["lock"] === "string" + ) +} + +export function isAlterTableOption(obj: unknown): obj is AlterTableOption { + const typedObj = obj as AlterTableOption + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "alter" && + typeof typedObj["resource"] === "string" && + typeof typedObj["keyword"] === "string" && + typeof typedObj["symbol"] === "string" && + (typeof typedObj["engine"] === "undefined" || + typeof typedObj["engine"] === "string") + ) +} + +export function isUse(obj: unknown): obj is Use { + const typedObj = obj as Use + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "use" && + typeof typedObj["db"] === "string" && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isKeywordComment(obj: unknown): obj is KeywordComment { + const typedObj = obj as KeywordComment + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "comment" && + typedObj["keyword"] === "comment" && + (typeof typedObj["symbol"] === "undefined" || + typedObj["symbol"] === null || + typedObj["symbol"] === "=") && + (typeof typedObj["value"] === "string" || + (typedObj["value"] !== null && + typeof typedObj["value"] === "object" || + typeof typedObj["value"] === "function") && + (typedObj["value"]["type"] === "string" || + typedObj["value"]["type"] === "number" || + typedObj["value"]["type"] === "boolean" || + typedObj["value"]["type"] === "backticks_quote_string" || + typedObj["value"]["type"] === "hex_string" || + typedObj["value"]["type"] === "full_hex_string" || + typedObj["value"]["type"] === "natural_string" || + typedObj["value"]["type"] === "bit_string" || + typedObj["value"]["type"] === "double_quote_string" || + typedObj["value"]["type"] === "single_quote_string" || + typedObj["value"]["type"] === "bool" || + typedObj["value"]["type"] === "null" || + typedObj["value"]["type"] === "star" || + typedObj["value"]["type"] === "param" || + typedObj["value"]["type"] === "origin" || + typedObj["value"]["type"] === "date" || + typedObj["value"]["type"] === "datetime" || + typedObj["value"]["type"] === "default" || + typedObj["value"]["type"] === "time" || + typedObj["value"]["type"] === "timestamp") && + (typeof typedObj["value"]["value"] === "string" || + typeof typedObj["value"]["value"] === "number" || + typedObj["value"]["value"] === false || + typedObj["value"]["value"] === true)) + ) +} + +export function isCollateExpr(obj: unknown): obj is CollateExpr { + const typedObj = obj as CollateExpr + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "collate" && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === "collate") && + (typeof typedObj["symbol"] === "undefined" || + typedObj["symbol"] === null || + typedObj["symbol"] === "=") && + (typeof typedObj["value"] === "undefined" || + typeof typedObj["value"] === "string") && + (typeof typedObj["collate"] === "undefined" || + (typedObj["collate"] !== null && + typeof typedObj["collate"] === "object" || + typeof typedObj["collate"] === "function") && + typeof typedObj["collate"]["name"] === "string" && + (typedObj["collate"]["symbol"] === null || + typedObj["collate"]["symbol"] === "=")) && + (typeof typedObj["name"] === "undefined" || + typeof typedObj["name"] === "string") + ) +} + +export function isDataType(obj: unknown): obj is DataType { + const typedObj = obj as DataType + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typeof typedObj["dataType"] === "string" && + (typeof typedObj["length"] === "undefined" || + typeof typedObj["length"] === "number") && + (typeof typedObj["parentheses"] === "undefined" || + typedObj["parentheses"] === true) && + (typeof typedObj["scale"] === "undefined" || + typeof typedObj["scale"] === "number") && + (typeof typedObj["suffix"] === "undefined" || + typedObj["suffix"] === null || + isOnUpdateCurrentTimestamp(typedObj["suffix"]) as boolean || + Array.isArray(typedObj["suffix"]) && + typedObj["suffix"].every((e: any) => + (e === "UNSIGNED" || + e === "ZEROFILL") + )) && + (typeof typedObj["expr"] === "undefined" || + isBinary(typedObj["expr"]) as boolean || + isUnary(typedObj["expr"]) as boolean || + isExprList(typedObj["expr"]) as boolean) && + (typeof typedObj["quoted"] === "undefined" || + typeof typedObj["quoted"] === "string") + ) +} + +export function isOnUpdateCurrentTimestamp(obj: unknown): obj is OnUpdateCurrentTimestamp { + const typedObj = obj as OnUpdateCurrentTimestamp + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "on_update_current_timestamp" && + typedObj["keyword"] === "on update" && + isFunction(typedObj["expr"]) as boolean + ) +} + +export function isLiteralNotNull(obj: unknown): obj is LiteralNotNull { + const typedObj = obj as LiteralNotNull + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "not null" && + typedObj["value"] === "not null" + ) +} + +export function isLiteralNull(obj: unknown): obj is LiteralNull { + const typedObj = obj as LiteralNull + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "null" && + (typedObj["value"] === null || + typedObj["value"] === "null") + ) +} + +export function isColumnConstraint(obj: unknown): obj is ColumnConstraint { + const typedObj = obj as ColumnConstraint + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["default_val"] !== null && + typeof typedObj["default_val"] === "object" || + typeof typedObj["default_val"] === "function") && + typedObj["default_val"]["type"] === "default" && + isExpressionValue(typedObj["default_val"]["value"]) as boolean && + (isLiteralNotNull(typedObj["nullable"]) as boolean || + isLiteralNull(typedObj["nullable"]) as boolean) + ) +} + +export function isColumnDefinitionOptList(obj: unknown): obj is ColumnDefinitionOptList { + const typedObj = obj as ColumnDefinitionOptList + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["nullable"] === "undefined" || + isLiteralNotNull(typedObj["nullable"]) as boolean || + isLiteralNull(typedObj["nullable"]) as boolean) && + (typeof typedObj["default_val"] === "undefined" || + (typedObj["default_val"] !== null && + typeof typedObj["default_val"] === "object" || + typeof typedObj["default_val"] === "function") && + typedObj["default_val"]["type"] === "default" && + isExpressionValue(typedObj["default_val"]["value"]) as boolean) && + (typeof typedObj["auto_increment"] === "undefined" || + typedObj["auto_increment"] === "auto_increment") && + (typeof typedObj["unique"] === "undefined" || + typedObj["unique"] === "unique" || + typedObj["unique"] === "unique key") && + (typeof typedObj["primary_key"] === "undefined" || + typedObj["primary_key"] === "key" || + typedObj["primary_key"] === "primary key") && + (typeof typedObj["comment"] === "undefined" || + isKeywordComment(typedObj["comment"]) as boolean) && + (typeof typedObj["collate"] === "undefined" || + isCollateExpr(typedObj["collate"]) as boolean) && + (typeof typedObj["column_format"] === "undefined" || + (typedObj["column_format"] !== null && + typeof typedObj["column_format"] === "object" || + typeof typedObj["column_format"] === "function") && + typeof typedObj["column_format"]["type"] === "string" && + typeof typedObj["column_format"]["value"] === "string") && + (typeof typedObj["storage"] === "undefined" || + (typedObj["storage"] !== null && + typeof typedObj["storage"] === "object" || + typeof typedObj["storage"] === "function") && + typeof typedObj["storage"]["type"] === "string" && + typeof typedObj["storage"]["value"] === "string") && + (typeof typedObj["reference_definition"] === "undefined" || + isReferenceDefinition(typedObj["reference_definition"]) as boolean) && + (typeof typedObj["character_set"] === "undefined" || + (typedObj["character_set"] !== null && + typeof typedObj["character_set"] === "object" || + typeof typedObj["character_set"] === "function") && + typedObj["character_set"]["type"] === "CHARACTER SET" && + (typedObj["character_set"]["value"] !== null && + typeof typedObj["character_set"]["value"] === "object" || + typeof typedObj["character_set"]["value"] === "function") && + (typedObj["character_set"]["value"]["type"] === "string" || + typedObj["character_set"]["value"]["type"] === "number" || + typedObj["character_set"]["value"]["type"] === "boolean" || + typedObj["character_set"]["value"]["type"] === "backticks_quote_string" || + typedObj["character_set"]["value"]["type"] === "hex_string" || + typedObj["character_set"]["value"]["type"] === "full_hex_string" || + typedObj["character_set"]["value"]["type"] === "natural_string" || + typedObj["character_set"]["value"]["type"] === "bit_string" || + typedObj["character_set"]["value"]["type"] === "double_quote_string" || + typedObj["character_set"]["value"]["type"] === "single_quote_string" || + typedObj["character_set"]["value"]["type"] === "bool" || + typedObj["character_set"]["value"]["type"] === "null" || + typedObj["character_set"]["value"]["type"] === "star" || + typedObj["character_set"]["value"]["type"] === "param" || + typedObj["character_set"]["value"]["type"] === "origin" || + typedObj["character_set"]["value"]["type"] === "date" || + typedObj["character_set"]["value"]["type"] === "datetime" || + typedObj["character_set"]["value"]["type"] === "default" || + typedObj["character_set"]["value"]["type"] === "time" || + typedObj["character_set"]["value"]["type"] === "timestamp") && + (typeof typedObj["character_set"]["value"]["value"] === "string" || + typeof typedObj["character_set"]["value"]["value"] === "number" || + typedObj["character_set"]["value"]["value"] === false || + typedObj["character_set"]["value"]["value"] === true) && + (typedObj["character_set"]["symbol"] === null || + typedObj["character_set"]["symbol"] === "=")) && + (typeof typedObj["check"] === "undefined" || + (typedObj["check"] !== null && + typeof typedObj["check"] === "object" || + typeof typedObj["check"] === "function") && + typedObj["check"]["constraint_type"] === "check" && + (typedObj["check"]["keyword"] === null || + typedObj["check"]["keyword"] === "constraint") && + (typedObj["check"]["constraint"] === null || + typeof typedObj["check"]["constraint"] === "string") && + Array.isArray(typedObj["check"]["definition"]) && + typedObj["check"]["definition"].every((e: any) => + isExpressionValue(e) as boolean + ) && + (typedObj["check"]["enforced"] === "" || + typedObj["check"]["enforced"] === "enforced" || + typedObj["check"]["enforced"] === "not enforced") && + typedObj["check"]["resource"] === "constraint") && + (typeof typedObj["generated"] === "undefined" || + (typedObj["generated"] !== null && + typeof typedObj["generated"] === "object" || + typeof typedObj["generated"] === "function") && + typedObj["generated"]["type"] === "generated" && + isExpressionValue(typedObj["generated"]["expr"]) as boolean && + typeof typedObj["generated"]["value"] === "string" && + (typeof typedObj["generated"]["storage_type"] === "undefined" || + typedObj["generated"]["storage_type"] === "stored" || + typedObj["generated"]["storage_type"] === "virtual")) + ) +} + +export function isReferenceDefinition(obj: unknown): obj is ReferenceDefinition { + const typedObj = obj as ReferenceDefinition + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["definition"] === "undefined" || + Array.isArray(typedObj["definition"]) && + typedObj["definition"].every((e: any) => + isColumnRefItem(e) as boolean + )) && + (typeof typedObj["table"] === "undefined" || + Array.isArray(typedObj["table"]) && + typedObj["table"].every((e: any) => + isFrom(e) as boolean + )) && + (typeof typedObj["keyword"] === "undefined" || + typeof typedObj["keyword"] === "string") && + (typeof typedObj["match"] === "undefined" || + typedObj["match"] === null || + typeof typedObj["match"] === "string") && + Array.isArray(typedObj["on_action"]) && + typedObj["on_action"].every((e: any) => + isOnReference(e) as boolean + ) + ) +} + +export function isOnReference(obj: unknown): obj is OnReference { + const typedObj = obj as OnReference + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["type"] === "on update" || + typedObj["type"] === "on delete") && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === "on update" || + typedObj["keyword"] === "on delete") && + ((typedObj["value"] !== null && + typeof typedObj["value"] === "object" || + typeof typedObj["value"] === "function") && + (typedObj["value"]["type"] === "string" || + typedObj["value"]["type"] === "number" || + typedObj["value"]["type"] === "boolean" || + typedObj["value"]["type"] === "backticks_quote_string" || + typedObj["value"]["type"] === "hex_string" || + typedObj["value"]["type"] === "full_hex_string" || + typedObj["value"]["type"] === "natural_string" || + typedObj["value"]["type"] === "bit_string" || + typedObj["value"]["type"] === "double_quote_string" || + typedObj["value"]["type"] === "single_quote_string" || + typedObj["value"]["type"] === "bool" || + typedObj["value"]["type"] === "null" || + typedObj["value"]["type"] === "star" || + typedObj["value"]["type"] === "param" || + typedObj["value"]["type"] === "origin" || + typedObj["value"]["type"] === "date" || + typedObj["value"]["type"] === "datetime" || + typedObj["value"]["type"] === "default" || + typedObj["value"]["type"] === "time" || + typedObj["value"]["type"] === "timestamp") && + (typeof typedObj["value"]["value"] === "string" || + typeof typedObj["value"]["value"] === "number" || + typedObj["value"]["value"] === false || + typedObj["value"]["value"] === true) || + typedObj["value"] === "restrict" || + typedObj["value"] === "cascade" || + typedObj["value"] === "set null" || + typedObj["value"] === "no action" || + typedObj["value"] === "set default") + ) +} + +export function isCreateColumnDefinition(obj: unknown): obj is CreateColumnDefinition { + const typedObj = obj as CreateColumnDefinition + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + isColumnRefItem(typedObj["column"]) as boolean && + isDataType(typedObj["definition"]) as boolean && + typedObj["resource"] === "column" && + isColumnDefinitionOptList(typedObj) as boolean + ) +} + +export function isIndexType(obj: unknown): obj is IndexType { + const typedObj = obj as IndexType + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["keyword"] === "using" && + (typedObj["type"] === "btree" || + typedObj["type"] === "hash" || + typedObj["type"] === "gist" || + typedObj["type"] === "gin") + ) +} + +export function isIndexOption(obj: unknown): obj is IndexOption { + const typedObj = obj as IndexOption + return ( + (isKeywordComment(typedObj) as boolean || + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "key_block_size" && + (typeof typedObj["symbol"] === "undefined" || + typedObj["symbol"] === "=") && + isValue(typedObj["expr"]) as boolean || + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["keyword"] === "using" && + (typedObj["type"] === "btree" || + typedObj["type"] === "hash") || + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "with parser" && + typeof typedObj["expr"] === "string" || + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["type"] === "visible" || + typedObj["type"] === "invisible") && + typeof typedObj["expr"] === "string") + ) +} + +export function isCreateIndexDefinition(obj: unknown): obj is CreateIndexDefinition { + const typedObj = obj as CreateIndexDefinition + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["index"] === null || + typeof typedObj["index"] === "string") && + Array.isArray(typedObj["definition"]) && + typedObj["definition"].every((e: any) => + isColumnRefItem(e) as boolean + ) && + (typedObj["keyword"] === "index" || + typedObj["keyword"] === "key") && + (typedObj["index_type"] === null || + isIndexType(typedObj["index_type"]) as boolean) && + typedObj["resource"] === "index" && + (typedObj["index_options"] === null || + Array.isArray(typedObj["index_options"]) && + typedObj["index_options"].every((e: any) => + isIndexOption(e) as boolean + )) + ) +} + +export function isCreateFulltextSpatialIndexDefinition(obj: unknown): obj is CreateFulltextSpatialIndexDefinition { + const typedObj = obj as CreateFulltextSpatialIndexDefinition + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["index"] === "undefined" || + typedObj["index"] === null || + typeof typedObj["index"] === "string") && + Array.isArray(typedObj["definition"]) && + typedObj["definition"].every((e: any) => + isColumnRefItem(e) as boolean + ) && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === "fulltext" || + typedObj["keyword"] === "spatial" || + typedObj["keyword"] === "fulltext key" || + typedObj["keyword"] === "spatial key" || + typedObj["keyword"] === "fulltext index" || + typedObj["keyword"] === "spatial index") && + (typeof typedObj["index_options"] === "undefined" || + typedObj["index_options"] === null || + Array.isArray(typedObj["index_options"]) && + typedObj["index_options"].every((e: any) => + isIndexOption(e) as boolean + )) && + typedObj["resource"] === "index" + ) +} + +export function isConstraintName(obj: unknown): obj is ConstraintName { + const typedObj = obj as ConstraintName + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["keyword"] === "constraint" && + typeof typedObj["constraint"] === "string" + ) +} + +export function isCreateConstraintPrimary(obj: unknown): obj is CreateConstraintPrimary { + const typedObj = obj as CreateConstraintPrimary + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["constraint"] === "undefined" || + typedObj["constraint"] === null || + typeof typedObj["constraint"] === "string") && + Array.isArray(typedObj["definition"]) && + typedObj["definition"].every((e: any) => + isColumnRefItem(e) as boolean + ) && + typedObj["constraint_type"] === "primary key" && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === null || + typedObj["keyword"] === "constraint") && + (typeof typedObj["index_type"] === "undefined" || + typedObj["index_type"] === null || + isIndexType(typedObj["index_type"]) as boolean) && + typedObj["resource"] === "constraint" && + (typeof typedObj["index_options"] === "undefined" || + typedObj["index_options"] === null || + Array.isArray(typedObj["index_options"]) && + typedObj["index_options"].every((e: any) => + isIndexOption(e) as boolean + )) + ) +} + +export function isCreateConstraintUnique(obj: unknown): obj is CreateConstraintUnique { + const typedObj = obj as CreateConstraintUnique + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["constraint"] === "undefined" || + typedObj["constraint"] === null || + typeof typedObj["constraint"] === "string") && + Array.isArray(typedObj["definition"]) && + typedObj["definition"].every((e: any) => + isColumnRefItem(e) as boolean + ) && + (typedObj["constraint_type"] === "unique" || + typedObj["constraint_type"] === "unique key" || + typedObj["constraint_type"] === "unique index") && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === null || + typedObj["keyword"] === "constraint") && + (typeof typedObj["index_type"] === "undefined" || + typedObj["index_type"] === null || + isIndexType(typedObj["index_type"]) as boolean) && + (typeof typedObj["index"] === "undefined" || + typedObj["index"] === null || + typeof typedObj["index"] === "string") && + typedObj["resource"] === "constraint" && + (typeof typedObj["index_options"] === "undefined" || + typedObj["index_options"] === null || + Array.isArray(typedObj["index_options"]) && + typedObj["index_options"].every((e: any) => + isIndexOption(e) as boolean + )) + ) +} + +export function isCreateConstraintForeign(obj: unknown): obj is CreateConstraintForeign { + const typedObj = obj as CreateConstraintForeign + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["constraint"] === "undefined" || + typedObj["constraint"] === null || + typeof typedObj["constraint"] === "string") && + Array.isArray(typedObj["definition"]) && + typedObj["definition"].every((e: any) => + isColumnRefItem(e) as boolean + ) && + (typedObj["constraint_type"] === "foreign key" || + typedObj["constraint_type"] === "FOREIGN KEY") && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === null || + typedObj["keyword"] === "constraint") && + (typeof typedObj["index"] === "undefined" || + typedObj["index"] === null || + typeof typedObj["index"] === "string") && + typedObj["resource"] === "constraint" && + (typeof typedObj["reference_definition"] === "undefined" || + isReferenceDefinition(typedObj["reference_definition"]) as boolean) + ) +} + +export function isCreateConstraintCheck(obj: unknown): obj is CreateConstraintCheck { + const typedObj = obj as CreateConstraintCheck + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typeof typedObj["constraint"] === "undefined" || + typedObj["constraint"] === null || + typeof typedObj["constraint"] === "string") && + Array.isArray(typedObj["definition"]) && + typedObj["definition"].every((e: any) => + isBinary(e) as boolean + ) && + typedObj["constraint_type"] === "check" && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === null || + typedObj["keyword"] === "constraint") && + typedObj["resource"] === "constraint" && + (typeof typedObj["index_type"] === "undefined" || + typedObj["index_type"] === null || + isIndexType(typedObj["index_type"]) as boolean) + ) +} + +export function isCreateConstraintDefinition(obj: unknown): obj is CreateConstraintDefinition { + const typedObj = obj as CreateConstraintDefinition + return ( + (isCreateConstraintPrimary(typedObj) as boolean || + isCreateConstraintUnique(typedObj) as boolean || + isCreateConstraintForeign(typedObj) as boolean || + isCreateConstraintCheck(typedObj) as boolean) + ) +} + +export function isCreateDefinition(obj: unknown): obj is CreateDefinition { + const typedObj = obj as CreateDefinition + return ( + (isCreateColumnDefinition(typedObj) as boolean || + isCreateIndexDefinition(typedObj) as boolean || + isCreateFulltextSpatialIndexDefinition(typedObj) as boolean || + isCreateConstraintPrimary(typedObj) as boolean || + isCreateConstraintUnique(typedObj) as boolean || + isCreateConstraintForeign(typedObj) as boolean || + isCreateConstraintCheck(typedObj) as boolean) + ) +} + +export function isCreateTable(obj: unknown): obj is CreateTable { + const typedObj = obj as CreateTable + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "create" && + typedObj["keyword"] === "table" && + (typedObj["temporary"] === null || + typedObj["temporary"] === "temporary") && + (Array.isArray(typedObj["table"]) && + typedObj["table"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["db"] === null || + typeof e["db"] === "string") && + typeof e["table"] === "string" + ) || + (typedObj["table"] !== null && + typeof typedObj["table"] === "object" || + typeof typedObj["table"] === "function") && + (typedObj["table"]["db"] === null || + typeof typedObj["table"]["db"] === "string") && + typeof typedObj["table"]["table"] === "string") && + (typedObj["if_not_exists"] === null || + typedObj["if_not_exists"] === "IF NOT EXISTS") && + (typeof typedObj["like"] === "undefined" || + typedObj["like"] === null || + (typedObj["like"] !== null && + typeof typedObj["like"] === "object" || + typeof typedObj["like"] === "function") && + typedObj["like"]["type"] === "like" && + Array.isArray(typedObj["like"]["table"]) && + typedObj["like"]["table"].every((e: any) => + isFrom(e) as boolean + ) && + (typeof typedObj["like"]["parentheses"] === "undefined" || + typedObj["like"]["parentheses"] === false || + typedObj["like"]["parentheses"] === true)) && + (typeof typedObj["ignore_replace"] === "undefined" || + typedObj["ignore_replace"] === null || + typedObj["ignore_replace"] === "replace" || + typedObj["ignore_replace"] === "ignore") && + (typeof typedObj["as"] === "undefined" || + typedObj["as"] === null || + typeof typedObj["as"] === "string") && + (typeof typedObj["query_expr"] === "undefined" || + typedObj["query_expr"] === null || + isSelect(typedObj["query_expr"]) as boolean) && + (typeof typedObj["create_definitions"] === "undefined" || + typedObj["create_definitions"] === null || + Array.isArray(typedObj["create_definitions"]) && + typedObj["create_definitions"].every((e: any) => + isCreateDefinition(e) as boolean + )) && + (typeof typedObj["table_options"] === "undefined" || + typedObj["table_options"] === null || + Array.isArray(typedObj["table_options"]) && + typedObj["table_options"].every((e: any) => + isTableOption(e) as boolean + )) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isCreateDatabase(obj: unknown): obj is CreateDatabase { + const typedObj = obj as CreateDatabase + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "create" && + typedObj["keyword"] === "database" && + (typeof typedObj["if_not_exists"] === "undefined" || + typedObj["if_not_exists"] === null || + typedObj["if_not_exists"] === "IF NOT EXISTS") && + (typeof typedObj["database"] === "undefined" || + typeof typedObj["database"] === "string" || + (typedObj["database"] !== null && + typeof typedObj["database"] === "object" || + typeof typedObj["database"] === "function") && + Array.isArray(typedObj["database"]["schema"]) && + typedObj["database"]["schema"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["type"] === "string" || + e["type"] === "number" || + e["type"] === "boolean" || + e["type"] === "backticks_quote_string" || + e["type"] === "hex_string" || + e["type"] === "full_hex_string" || + e["type"] === "natural_string" || + e["type"] === "bit_string" || + e["type"] === "double_quote_string" || + e["type"] === "single_quote_string" || + e["type"] === "bool" || + e["type"] === "null" || + e["type"] === "star" || + e["type"] === "param" || + e["type"] === "origin" || + e["type"] === "date" || + e["type"] === "datetime" || + e["type"] === "default" || + e["type"] === "time" || + e["type"] === "timestamp") && + (typeof e["value"] === "string" || + typeof e["value"] === "number" || + e["value"] === false || + e["value"] === true) + )) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isCreateSchema(obj: unknown): obj is CreateSchema { + const typedObj = obj as CreateSchema + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "create" && + typedObj["keyword"] === "schema" && + (typeof typedObj["if_not_exists"] === "undefined" || + typedObj["if_not_exists"] === null || + typedObj["if_not_exists"] === "IF NOT EXISTS") && + (typeof typedObj["database"] === "undefined" || + typeof typedObj["database"] === "string" || + (typedObj["database"] !== null && + typeof typedObj["database"] === "object" || + typeof typedObj["database"] === "function") && + Array.isArray(typedObj["database"]["schema"]) && + typedObj["database"]["schema"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["type"] === "string" || + e["type"] === "number" || + e["type"] === "boolean" || + e["type"] === "backticks_quote_string" || + e["type"] === "hex_string" || + e["type"] === "full_hex_string" || + e["type"] === "natural_string" || + e["type"] === "bit_string" || + e["type"] === "double_quote_string" || + e["type"] === "single_quote_string" || + e["type"] === "bool" || + e["type"] === "null" || + e["type"] === "star" || + e["type"] === "param" || + e["type"] === "origin" || + e["type"] === "date" || + e["type"] === "datetime" || + e["type"] === "default" || + e["type"] === "time" || + e["type"] === "timestamp") && + (typeof e["value"] === "string" || + typeof e["value"] === "number" || + e["value"] === false || + e["value"] === true) + )) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isCreateIndex(obj: unknown): obj is CreateIndex { + const typedObj = obj as CreateIndex + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "create" && + typedObj["keyword"] === "index" && + (typeof typedObj["index_using"] === "undefined" || + typedObj["index_using"] === null || + (typedObj["index_using"] !== null && + typeof typedObj["index_using"] === "object" || + typeof typedObj["index_using"] === "function") && + typedObj["index_using"]["keyword"] === "using" && + (typedObj["index_using"]["type"] === "btree" || + typedObj["index_using"]["type"] === "hash")) && + (typeof typedObj["index"] === "undefined" || + typedObj["index"] === null || + typeof typedObj["index"] === "string") && + (typeof typedObj["on_kw"] === "undefined" || + typedObj["on_kw"] === null || + typedObj["on_kw"] === "on") && + (typeof typedObj["table"] === "undefined" || + Array.isArray(typedObj["table"]) && + typedObj["table"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["db"] === null || + typeof e["db"] === "string") && + typeof e["table"] === "string" + ) || + (typedObj["table"] !== null && + typeof typedObj["table"] === "object" || + typeof typedObj["table"] === "function") && + (typedObj["table"]["db"] === null || + typeof typedObj["table"]["db"] === "string") && + typeof typedObj["table"]["table"] === "string") && + (typeof typedObj["index_columns"] === "undefined" || + typedObj["index_columns"] === null || + Array.isArray(typedObj["index_columns"]) && + typedObj["index_columns"].every((e: any) => + isColumnRefItem(e) as boolean + )) && + (typeof typedObj["index_type"] === "undefined" || + typedObj["index_type"] === null || + typedObj["index_type"] === "unique" || + typedObj["index_type"] === "fulltext" || + typedObj["index_type"] === "spatial") && + (typeof typedObj["index_options"] === "undefined" || + typedObj["index_options"] === null || + Array.isArray(typedObj["index_options"]) && + typedObj["index_options"].every((e: any) => + isIndexOption(e) as boolean + )) && + (typeof typedObj["algorithm_option"] === "undefined" || + typedObj["algorithm_option"] === null || + (typedObj["algorithm_option"] !== null && + typeof typedObj["algorithm_option"] === "object" || + typeof typedObj["algorithm_option"] === "function") && + typedObj["algorithm_option"]["type"] === "alter" && + typedObj["algorithm_option"]["keyword"] === "algorithm" && + typedObj["algorithm_option"]["resource"] === "algorithm" && + (typedObj["algorithm_option"]["symbol"] === null || + typedObj["algorithm_option"]["symbol"] === "=") && + typeof typedObj["algorithm_option"]["algorithm"] === "string") && + (typeof typedObj["lock_option"] === "undefined" || + typedObj["lock_option"] === null || + (typedObj["lock_option"] !== null && + typeof typedObj["lock_option"] === "object" || + typeof typedObj["lock_option"] === "function") && + typedObj["lock_option"]["type"] === "alter" && + typedObj["lock_option"]["keyword"] === "lock" && + typedObj["lock_option"]["resource"] === "lock" && + (typedObj["lock_option"]["symbol"] === null || + typedObj["lock_option"]["symbol"] === "=") && + typeof typedObj["lock_option"]["lock"] === "string") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isCreateView(obj: unknown): obj is CreateView { + const typedObj = obj as CreateView + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "create" && + typedObj["keyword"] === "view" && + (typedObj["replace"] === null || + typedObj["replace"] === "or replace") && + (typedObj["algorithm"] === null || + typedObj["algorithm"] === "UNDEFINED" || + typedObj["algorithm"] === "MERGE" || + typedObj["algorithm"] === "TEMPTABLE") && + (typedObj["definer"] === null || + isBinary(typedObj["definer"]) as boolean) && + (typedObj["sql_security"] === null || + typedObj["sql_security"] === "DEFINER" || + typedObj["sql_security"] === "INVOKER") && + (typedObj["view"] !== null && + typeof typedObj["view"] === "object" || + typeof typedObj["view"] === "function") && + (typedObj["view"]["db"] === null || + typeof typedObj["view"]["db"] === "string") && + typeof typedObj["view"]["view"] === "string" && + (typedObj["columns"] === null || + Array.isArray(typedObj["columns"]) && + typedObj["columns"].every((e: any) => + typeof e === "string" + )) && + isSelect(typedObj["select"]) as boolean && + (typedObj["with"] === null || + typedObj["with"] === "with check option" || + typedObj["with"] === "with cascaded check option" || + typedObj["with"] === "with local check option") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isCreateTrigger(obj: unknown): obj is CreateTrigger { + const typedObj = obj as CreateTrigger + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "create" && + typedObj["keyword"] === "trigger" && + (typedObj["definer"] === null || + isBinary(typedObj["definer"]) as boolean) && + (typedObj["trigger"] !== null && + typeof typedObj["trigger"] === "object" || + typeof typedObj["trigger"] === "function") && + (typedObj["trigger"]["db"] === null || + typeof typedObj["trigger"]["db"] === "string") && + typeof typedObj["trigger"]["table"] === "string" && + typeof typedObj["time"] === "string" && + Array.isArray(typedObj["events"]) && + typedObj["events"].every((e: any) => + isTriggerEvent(e) as boolean + ) && + (typedObj["table"] !== null && + typeof typedObj["table"] === "object" || + typeof typedObj["table"] === "function") && + (typedObj["table"]["db"] === null || + typeof typedObj["table"]["db"] === "string") && + typeof typedObj["table"]["table"] === "string" && + (typedObj["for_each"] !== null && + typeof typedObj["for_each"] === "object" || + typeof typedObj["for_each"] === "function") && + typeof typedObj["for_each"]["keyword"] === "string" && + typeof typedObj["for_each"]["args"] === "string" && + (typedObj["order"] === null || + (typedObj["order"] !== null && + typeof typedObj["order"] === "object" || + typeof typedObj["order"] === "function") && + (typedObj["order"]["keyword"] === "FOLLOWS" || + typedObj["order"]["keyword"] === "PRECEDES") && + typeof typedObj["order"]["trigger"] === "string") && + (typedObj["execute"] !== null && + typeof typedObj["execute"] === "object" || + typeof typedObj["execute"] === "function") && + typedObj["execute"]["type"] === "set" && + Array.isArray(typedObj["execute"]["expr"]) && + typedObj["execute"]["expr"].every((e: any) => + isSetList(e) as boolean + ) && + (typedObj["if_not_exists"] === null || + typeof typedObj["if_not_exists"] === "string") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isCreateUser(obj: unknown): obj is CreateUser { + const typedObj = obj as CreateUser + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "create" && + typedObj["keyword"] === "user" && + (typeof typedObj["if_not_exists"] === "undefined" || + typedObj["if_not_exists"] === null || + typedObj["if_not_exists"] === "IF NOT EXISTS") && + (typeof typedObj["user"] === "undefined" || + typedObj["user"] === null || + Array.isArray(typedObj["user"]) && + typedObj["user"].every((e: any) => + isUserAuthOption(e) as boolean + )) && + (typeof typedObj["default_role"] === "undefined" || + typedObj["default_role"] === null || + Array.isArray(typedObj["default_role"]) && + typedObj["default_role"].every((e: any) => + typeof e === "string" + )) && + (typeof typedObj["require"] === "undefined" || + typedObj["require"] === null || + isRequireOption(typedObj["require"]) as boolean) && + (typeof typedObj["resource_options"] === "undefined" || + typedObj["resource_options"] === null || + isResourceOption(typedObj["resource_options"]) as boolean) && + (typeof typedObj["password_options"] === "undefined" || + typedObj["password_options"] === null || + isPasswordOption(typedObj["password_options"]) as boolean) && + (typeof typedObj["lock_option_user"] === "undefined" || + typedObj["lock_option_user"] === null || + typedObj["lock_option_user"] === "account lock" || + typedObj["lock_option_user"] === "account unlock") && + (typeof typedObj["comment_user"] === "undefined" || + typedObj["comment_user"] === null || + typeof typedObj["comment_user"] === "string") && + (typeof typedObj["attribute"] === "undefined" || + typedObj["attribute"] === null || + typeof typedObj["attribute"] === "string") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isCreate(obj: unknown): obj is Create { + const typedObj = obj as Create + return ( + (isCreateTable(typedObj) as boolean || + isCreateDatabase(typedObj) as boolean || + isCreateSchema(typedObj) as boolean || + isCreateIndex(typedObj) as boolean || + isCreateView(typedObj) as boolean || + isCreateTrigger(typedObj) as boolean || + isCreateUser(typedObj) as boolean) + ) +} + +export function isTriggerEvent(obj: unknown): obj is TriggerEvent { + const typedObj = obj as TriggerEvent + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["keyword"] === "insert" || + typedObj["keyword"] === "update" || + typedObj["keyword"] === "delete") && + (typeof typedObj["args"] === "undefined" || + Array.isArray(typedObj["args"]) && + typedObj["args"].every((e: any) => + isColumnRefItem(e) as boolean + )) + ) +} + +export function isUserAuthOption(obj: unknown): obj is UserAuthOption { + const typedObj = obj as UserAuthOption + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["user"] !== null && + typeof typedObj["user"] === "object" || + typeof typedObj["user"] === "function") && + (typedObj["user"]["name"] !== null && + typeof typedObj["user"]["name"] === "object" || + typeof typedObj["user"]["name"] === "function") && + (typedObj["user"]["name"]["type"] === "string" || + typedObj["user"]["name"]["type"] === "number" || + typedObj["user"]["name"]["type"] === "boolean" || + typedObj["user"]["name"]["type"] === "backticks_quote_string" || + typedObj["user"]["name"]["type"] === "hex_string" || + typedObj["user"]["name"]["type"] === "full_hex_string" || + typedObj["user"]["name"]["type"] === "natural_string" || + typedObj["user"]["name"]["type"] === "bit_string" || + typedObj["user"]["name"]["type"] === "double_quote_string" || + typedObj["user"]["name"]["type"] === "single_quote_string" || + typedObj["user"]["name"]["type"] === "bool" || + typedObj["user"]["name"]["type"] === "null" || + typedObj["user"]["name"]["type"] === "star" || + typedObj["user"]["name"]["type"] === "param" || + typedObj["user"]["name"]["type"] === "origin" || + typedObj["user"]["name"]["type"] === "date" || + typedObj["user"]["name"]["type"] === "datetime" || + typedObj["user"]["name"]["type"] === "default" || + typedObj["user"]["name"]["type"] === "time" || + typedObj["user"]["name"]["type"] === "timestamp") && + (typeof typedObj["user"]["name"]["value"] === "string" || + typeof typedObj["user"]["name"]["value"] === "number" || + typedObj["user"]["name"]["value"] === false || + typedObj["user"]["name"]["value"] === true) && + (typedObj["user"]["host"] !== null && + typeof typedObj["user"]["host"] === "object" || + typeof typedObj["user"]["host"] === "function") && + (typedObj["user"]["host"]["type"] === "string" || + typedObj["user"]["host"]["type"] === "number" || + typedObj["user"]["host"]["type"] === "boolean" || + typedObj["user"]["host"]["type"] === "backticks_quote_string" || + typedObj["user"]["host"]["type"] === "hex_string" || + typedObj["user"]["host"]["type"] === "full_hex_string" || + typedObj["user"]["host"]["type"] === "natural_string" || + typedObj["user"]["host"]["type"] === "bit_string" || + typedObj["user"]["host"]["type"] === "double_quote_string" || + typedObj["user"]["host"]["type"] === "single_quote_string" || + typedObj["user"]["host"]["type"] === "bool" || + typedObj["user"]["host"]["type"] === "null" || + typedObj["user"]["host"]["type"] === "star" || + typedObj["user"]["host"]["type"] === "param" || + typedObj["user"]["host"]["type"] === "origin" || + typedObj["user"]["host"]["type"] === "date" || + typedObj["user"]["host"]["type"] === "datetime" || + typedObj["user"]["host"]["type"] === "default" || + typedObj["user"]["host"]["type"] === "time" || + typedObj["user"]["host"]["type"] === "timestamp") && + (typeof typedObj["user"]["host"]["value"] === "string" || + typeof typedObj["user"]["host"]["value"] === "number" || + typedObj["user"]["host"]["value"] === false || + typedObj["user"]["host"]["value"] === true) && + (typeof typedObj["auth_option"] === "undefined" || + typedObj["auth_option"] === null || + (typedObj["auth_option"] !== null && + typeof typedObj["auth_option"] === "object" || + typeof typedObj["auth_option"] === "function") && + typeof typedObj["auth_option"]["keyword"] === "string" && + (typeof typedObj["auth_option"]["auth_plugin"] === "undefined" || + typedObj["auth_option"]["auth_plugin"] === null || + typeof typedObj["auth_option"]["auth_plugin"] === "string") && + (typedObj["auth_option"]["value"] !== null && + typeof typedObj["auth_option"]["value"] === "object" || + typeof typedObj["auth_option"]["value"] === "function") && + (typedObj["auth_option"]["value"]["type"] === "string" || + typedObj["auth_option"]["value"]["type"] === "number" || + typedObj["auth_option"]["value"]["type"] === "boolean" || + typedObj["auth_option"]["value"]["type"] === "backticks_quote_string" || + typedObj["auth_option"]["value"]["type"] === "hex_string" || + typedObj["auth_option"]["value"]["type"] === "full_hex_string" || + typedObj["auth_option"]["value"]["type"] === "natural_string" || + typedObj["auth_option"]["value"]["type"] === "bit_string" || + typedObj["auth_option"]["value"]["type"] === "double_quote_string" || + typedObj["auth_option"]["value"]["type"] === "single_quote_string" || + typedObj["auth_option"]["value"]["type"] === "bool" || + typedObj["auth_option"]["value"]["type"] === "null" || + typedObj["auth_option"]["value"]["type"] === "star" || + typedObj["auth_option"]["value"]["type"] === "param" || + typedObj["auth_option"]["value"]["type"] === "origin" || + typedObj["auth_option"]["value"]["type"] === "date" || + typedObj["auth_option"]["value"]["type"] === "datetime" || + typedObj["auth_option"]["value"]["type"] === "default" || + typedObj["auth_option"]["value"]["type"] === "time" || + typedObj["auth_option"]["value"]["type"] === "timestamp") && + (typeof typedObj["auth_option"]["value"]["value"] === "string" || + typeof typedObj["auth_option"]["value"]["value"] === "number" || + typedObj["auth_option"]["value"]["value"] === false || + typedObj["auth_option"]["value"]["value"] === true) && + (typedObj["auth_option"]["value"] !== null && + typeof typedObj["auth_option"]["value"] === "object" || + typeof typedObj["auth_option"]["value"] === "function") && + (typeof typedObj["auth_option"]["value"]["prefix"] === "undefined" || + typeof typedObj["auth_option"]["value"]["prefix"] === "string")) + ) +} + +export function isRequireOption(obj: unknown): obj is RequireOption { + const typedObj = obj as RequireOption + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["keyword"] === "require" && + (typedObj["value"] !== null && + typeof typedObj["value"] === "object" || + typeof typedObj["value"] === "function") && + (typedObj["value"]["type"] === "string" || + typedObj["value"]["type"] === "number" || + typedObj["value"]["type"] === "boolean" || + typedObj["value"]["type"] === "backticks_quote_string" || + typedObj["value"]["type"] === "hex_string" || + typedObj["value"]["type"] === "full_hex_string" || + typedObj["value"]["type"] === "natural_string" || + typedObj["value"]["type"] === "bit_string" || + typedObj["value"]["type"] === "double_quote_string" || + typedObj["value"]["type"] === "single_quote_string" || + typedObj["value"]["type"] === "bool" || + typedObj["value"]["type"] === "null" || + typedObj["value"]["type"] === "star" || + typedObj["value"]["type"] === "param" || + typedObj["value"]["type"] === "origin" || + typedObj["value"]["type"] === "date" || + typedObj["value"]["type"] === "datetime" || + typedObj["value"]["type"] === "default" || + typedObj["value"]["type"] === "time" || + typedObj["value"]["type"] === "timestamp") && + (typeof typedObj["value"]["value"] === "string" || + typeof typedObj["value"]["value"] === "number" || + typedObj["value"]["value"] === false || + typedObj["value"]["value"] === true) + ) +} + +export function isResourceOption(obj: unknown): obj is ResourceOption { + const typedObj = obj as ResourceOption + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["keyword"] === "with" && + Array.isArray(typedObj["value"]) && + typedObj["value"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["type"] === "string" && + typeof e["value"] === "number" && + typeof e["prefix"] === "string" + ) + ) +} + +export function isPasswordOption(obj: unknown): obj is PasswordOption { + const typedObj = obj as PasswordOption + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["type"] === "password_expire" || + typedObj["type"] === "password_history" || + typedObj["type"] === "password_reuse_interval" || + typedObj["type"] === "password_require_current") && + (typedObj["value"] === null || + typeof typedObj["value"] === "string" || + typeof typedObj["value"] === "number") + ) +} + +export function isTableOption(obj: unknown): obj is TableOption { + const typedObj = obj as TableOption + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typeof typedObj["keyword"] === "string" && + (typeof typedObj["symbol"] === "undefined" || + typedObj["symbol"] === "=") && + (typeof typedObj["value"] === "string" || + typeof typedObj["value"] === "number" || + isTableColumnAst(typedObj["value"]) as boolean || + isColumnRefItem(typedObj["value"]) as boolean || + isStar(typedObj["value"]) as boolean || + isCase(typedObj["value"]) as boolean || + isCast(typedObj["value"]) as boolean || + isAggrFunc(typedObj["value"]) as boolean || + isFunction(typedObj["value"]) as boolean || + isInterval(typedObj["value"]) as boolean || + isParam(typedObj["value"]) as boolean || + isVar(typedObj["value"]) as boolean || + isValue(typedObj["value"]) as boolean || + isBinary(typedObj["value"]) as boolean || + isUnary(typedObj["value"]) as boolean) + ) +} + +export function isDropTable(obj: unknown): obj is DropTable { + const typedObj = obj as DropTable + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "drop" && + typedObj["keyword"] === "table" && + Array.isArray(typedObj["name"]) && + typedObj["name"].every((e: any) => + isFrom(e) as boolean + ) && + (typedObj["prefix"] === null || + typedObj["prefix"] === "if exists") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isDropDatabase(obj: unknown): obj is DropDatabase { + const typedObj = obj as DropDatabase + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "drop" && + (typedObj["keyword"] === "database" || + typedObj["keyword"] === "schema") && + typeof typedObj["name"] === "string" && + (typedObj["prefix"] === null || + typedObj["prefix"] === "if exists") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isDropView(obj: unknown): obj is DropView { + const typedObj = obj as DropView + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "drop" && + typedObj["keyword"] === "view" && + Array.isArray(typedObj["name"]) && + typedObj["name"].every((e: any) => + isFrom(e) as boolean + ) && + (typedObj["prefix"] === null || + typedObj["prefix"] === "if exists") && + (typedObj["options"] === null || + typedObj["options"] === "restrict" || + typedObj["options"] === "cascade") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isDropIndex(obj: unknown): obj is DropIndex { + const typedObj = obj as DropIndex + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "drop" && + typedObj["keyword"] === "index" && + isColumnRefItem(typedObj["name"]) as boolean && + isFrom(typedObj["table"]) as boolean && + (typedObj["options"] === null || + typedObj["options"] === "restrict" || + typedObj["options"] === "cascade") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isDropTrigger(obj: unknown): obj is DropTrigger { + const typedObj = obj as DropTrigger + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "drop" && + typedObj["keyword"] === "trigger" && + Array.isArray(typedObj["name"]) && + typedObj["name"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["schema"] === null || + typeof e["schema"] === "string") && + typeof e["trigger"] === "string" + ) && + (typedObj["prefix"] === null || + typedObj["prefix"] === "if exists") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isDrop(obj: unknown): obj is Drop { + const typedObj = obj as Drop + return ( + (isDropTable(typedObj) as boolean || + isDropDatabase(typedObj) as boolean || + isDropView(typedObj) as boolean || + isDropIndex(typedObj) as boolean || + isDropTrigger(typedObj) as boolean) + ) +} + +export function isShow(obj: unknown): obj is Show { + const typedObj = obj as Show + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "show" && + typeof typedObj["keyword"] === "string" && + (typeof typedObj["suffix"] === "undefined" || + typeof typedObj["suffix"] === "string") && + (typeof typedObj["from"] === "undefined" || + isBaseFrom(typedObj["from"]) as boolean || + isJoin(typedObj["from"]) as boolean || + isTableExpr(typedObj["from"]) as boolean || + isDual(typedObj["from"]) as boolean) && + (typeof typedObj["where"] === "undefined" || + typedObj["where"] === null || + isFunction(typedObj["where"]) as boolean || + isBinary(typedObj["where"]) as boolean) && + (typeof typedObj["like"] === "undefined" || + typedObj["like"] === null || + (typedObj["like"] !== null && + typeof typedObj["like"] === "object" || + typeof typedObj["like"] === "function") && + typedObj["like"]["type"] === "like" && + typeof typedObj["like"]["value"] === "string") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isDesc(obj: unknown): obj is Desc { + const typedObj = obj as Desc + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "desc" && + typeof typedObj["table"] === "string" && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isExplain(obj: unknown): obj is Explain { + const typedObj = obj as Explain + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "explain" && + (isSelect(typedObj["expr"]) as boolean || + isInsert_Replace(typedObj["expr"]) as boolean || + isUpdate(typedObj["expr"]) as boolean || + isDelete(typedObj["expr"]) as boolean) && + (typeof typedObj["format"] === "undefined" || + typeof typedObj["format"] === "string") && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isCall(obj: unknown): obj is Call { + const typedObj = obj as Call + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "call" && + isFunction(typedObj["expr"]) as boolean && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isSet(obj: unknown): obj is Set { + const typedObj = obj as Set + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "set" && + (typeof typedObj["keyword"] === "undefined" || + typedObj["keyword"] === null || + typeof typedObj["keyword"] === "string") && + Array.isArray(typedObj["expr"]) && + typedObj["expr"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + e["type"] === "assign" && + isVar(e["left"]) as boolean && + typeof e["symbol"] === "string" && + isExpressionValue(e["right"]) as boolean + ) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isLock(obj: unknown): obj is Lock { + const typedObj = obj as Lock + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "lock" && + typedObj["keyword"] === "tables" && + Array.isArray(typedObj["tables"]) && + typedObj["tables"].every((e: any) => + isLockTable(e) as boolean + ) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isLockTable(obj: unknown): obj is LockTable { + const typedObj = obj as LockTable + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + isFrom(typedObj["table"]) as boolean && + (typedObj["lock_type"] !== null && + typeof typedObj["lock_type"] === "object" || + typeof typedObj["lock_type"] === "function") && + (typedObj["lock_type"]["type"] === "read" || + typedObj["lock_type"]["type"] === "write") && + (typeof typedObj["lock_type"]["suffix"] === "undefined" || + typedObj["lock_type"]["suffix"] === null) && + (typeof typedObj["lock_type"]["prefix"] === "undefined" || + typedObj["lock_type"]["prefix"] === null) + ) +} + +export function isUnlock(obj: unknown): obj is Unlock { + const typedObj = obj as Unlock + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "unlock" && + typedObj["keyword"] === "tables" && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isGrant(obj: unknown): obj is Grant { + const typedObj = obj as Grant + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "grant" && + typedObj["keyword"] === "priv" && + Array.isArray(typedObj["objects"]) && + typedObj["objects"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["priv"] !== null && + typeof e["priv"] === "object" || + typeof e["priv"] === "function") && + (e["priv"]["type"] === "string" || + e["priv"]["type"] === "number" || + e["priv"]["type"] === "boolean" || + e["priv"]["type"] === "backticks_quote_string" || + e["priv"]["type"] === "hex_string" || + e["priv"]["type"] === "full_hex_string" || + e["priv"]["type"] === "natural_string" || + e["priv"]["type"] === "bit_string" || + e["priv"]["type"] === "double_quote_string" || + e["priv"]["type"] === "single_quote_string" || + e["priv"]["type"] === "bool" || + e["priv"]["type"] === "null" || + e["priv"]["type"] === "star" || + e["priv"]["type"] === "param" || + e["priv"]["type"] === "origin" || + e["priv"]["type"] === "date" || + e["priv"]["type"] === "datetime" || + e["priv"]["type"] === "default" || + e["priv"]["type"] === "time" || + e["priv"]["type"] === "timestamp") && + (typeof e["priv"]["value"] === "string" || + typeof e["priv"]["value"] === "number" || + e["priv"]["value"] === false || + e["priv"]["value"] === true) && + (e["columns"] === null || + Array.isArray(e["columns"]) && + e["columns"].every((e: any) => + isColumnRefItem(e) as boolean + )) + ) && + (typedObj["on"] !== null && + typeof typedObj["on"] === "object" || + typeof typedObj["on"] === "function") && + (typedObj["on"]["object_type"] === null || + typedObj["on"]["object_type"] === "function" || + typedObj["on"]["object_type"] === "table" || + typedObj["on"]["object_type"] === "procedure") && + Array.isArray(typedObj["on"]["priv_level"]) && + typedObj["on"]["priv_level"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["prefix"] === "string" && + typeof e["name"] === "string" + ) && + (typedObj["to_from"] === "TO" || + typedObj["to_from"] === "FROM") && + Array.isArray(typedObj["user_or_roles"]) && + typedObj["user_or_roles"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["name"] !== null && + typeof e["name"] === "object" || + typeof e["name"] === "function") && + (e["name"]["type"] === "string" || + e["name"]["type"] === "number" || + e["name"]["type"] === "boolean" || + e["name"]["type"] === "backticks_quote_string" || + e["name"]["type"] === "hex_string" || + e["name"]["type"] === "full_hex_string" || + e["name"]["type"] === "natural_string" || + e["name"]["type"] === "bit_string" || + e["name"]["type"] === "double_quote_string" || + e["name"]["type"] === "single_quote_string" || + e["name"]["type"] === "bool" || + e["name"]["type"] === "null" || + e["name"]["type"] === "star" || + e["name"]["type"] === "param" || + e["name"]["type"] === "origin" || + e["name"]["type"] === "date" || + e["name"]["type"] === "datetime" || + e["name"]["type"] === "default" || + e["name"]["type"] === "time" || + e["name"]["type"] === "timestamp") && + (typeof e["name"]["value"] === "string" || + typeof e["name"]["value"] === "number" || + e["name"]["value"] === false || + e["name"]["value"] === true) && + (e["host"] === null || + (e["host"] !== null && + typeof e["host"] === "object" || + typeof e["host"] === "function") && + (e["host"]["type"] === "string" || + e["host"]["type"] === "number" || + e["host"]["type"] === "boolean" || + e["host"]["type"] === "backticks_quote_string" || + e["host"]["type"] === "hex_string" || + e["host"]["type"] === "full_hex_string" || + e["host"]["type"] === "natural_string" || + e["host"]["type"] === "bit_string" || + e["host"]["type"] === "double_quote_string" || + e["host"]["type"] === "single_quote_string" || + e["host"]["type"] === "bool" || + e["host"]["type"] === "null" || + e["host"]["type"] === "star" || + e["host"]["type"] === "param" || + e["host"]["type"] === "origin" || + e["host"]["type"] === "date" || + e["host"]["type"] === "datetime" || + e["host"]["type"] === "default" || + e["host"]["type"] === "time" || + e["host"]["type"] === "timestamp") && + (typeof e["host"]["value"] === "string" || + typeof e["host"]["value"] === "number" || + e["host"]["value"] === false || + e["host"]["value"] === true)) + ) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isLoadData(obj: unknown): obj is LoadData { + const typedObj = obj as LoadData + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "load_data" && + (typeof typedObj["mode"] === "undefined" || + typedObj["mode"] === null || + typeof typedObj["mode"] === "string") && + (typeof typedObj["local"] === "undefined" || + typedObj["local"] === null || + typedObj["local"] === "local") && + (typedObj["file"] !== null && + typeof typedObj["file"] === "object" || + typeof typedObj["file"] === "function") && + (typedObj["file"]["type"] === "string" || + typedObj["file"]["type"] === "number" || + typedObj["file"]["type"] === "boolean" || + typedObj["file"]["type"] === "backticks_quote_string" || + typedObj["file"]["type"] === "hex_string" || + typedObj["file"]["type"] === "full_hex_string" || + typedObj["file"]["type"] === "natural_string" || + typedObj["file"]["type"] === "bit_string" || + typedObj["file"]["type"] === "double_quote_string" || + typedObj["file"]["type"] === "single_quote_string" || + typedObj["file"]["type"] === "bool" || + typedObj["file"]["type"] === "null" || + typedObj["file"]["type"] === "star" || + typedObj["file"]["type"] === "param" || + typedObj["file"]["type"] === "origin" || + typedObj["file"]["type"] === "date" || + typedObj["file"]["type"] === "datetime" || + typedObj["file"]["type"] === "default" || + typedObj["file"]["type"] === "time" || + typedObj["file"]["type"] === "timestamp") && + (typeof typedObj["file"]["value"] === "string" || + typeof typedObj["file"]["value"] === "number" || + typedObj["file"]["value"] === false || + typedObj["file"]["value"] === true) && + (typeof typedObj["replace_ignore"] === "undefined" || + typedObj["replace_ignore"] === null || + typedObj["replace_ignore"] === "replace" || + typedObj["replace_ignore"] === "ignore") && + (typedObj["table"] !== null && + typeof typedObj["table"] === "object" || + typeof typedObj["table"] === "function") && + (typedObj["table"]["db"] === null || + typeof typedObj["table"]["db"] === "string") && + typeof typedObj["table"]["table"] === "string" && + (typeof typedObj["partition"] === "undefined" || + typedObj["partition"] === null || + Array.isArray(typedObj["partition"]) && + typedObj["partition"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["type"] === "string" || + e["type"] === "number" || + e["type"] === "boolean" || + e["type"] === "backticks_quote_string" || + e["type"] === "hex_string" || + e["type"] === "full_hex_string" || + e["type"] === "natural_string" || + e["type"] === "bit_string" || + e["type"] === "double_quote_string" || + e["type"] === "single_quote_string" || + e["type"] === "bool" || + e["type"] === "null" || + e["type"] === "star" || + e["type"] === "param" || + e["type"] === "origin" || + e["type"] === "date" || + e["type"] === "datetime" || + e["type"] === "default" || + e["type"] === "time" || + e["type"] === "timestamp") && + typeof e["value"] === "string" + )) && + (typeof typedObj["character_set"] === "undefined" || + typedObj["character_set"] === null || + typeof typedObj["character_set"] === "string") && + (typeof typedObj["fields"] === "undefined" || + typedObj["fields"] === null || + isLoadDataField(typedObj["fields"]) as boolean) && + (typeof typedObj["lines"] === "undefined" || + typedObj["lines"] === null || + isLoadDataLine(typedObj["lines"]) as boolean) && + (typeof typedObj["ignore"] === "undefined" || + typedObj["ignore"] === null || + typeof typedObj["ignore"] === "number") && + (typeof typedObj["column"] === "undefined" || + typedObj["column"] === null || + Array.isArray(typedObj["column"]) && + typedObj["column"].every((e: any) => + isColumnRefItem(e) as boolean + )) && + (typeof typedObj["set"] === "undefined" || + typedObj["set"] === null || + Array.isArray(typedObj["set"]) && + typedObj["set"].every((e: any) => + isSetList(e) as boolean + )) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isLoadDataField(obj: unknown): obj is LoadDataField { + const typedObj = obj as LoadDataField + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["keyword"] === "FIELDS" && + (typedObj["terminated"] === null || + (typedObj["terminated"] !== null && + typeof typedObj["terminated"] === "object" || + typeof typedObj["terminated"] === "function") && + (typedObj["terminated"]["type"] === "string" || + typedObj["terminated"]["type"] === "number" || + typedObj["terminated"]["type"] === "boolean" || + typedObj["terminated"]["type"] === "backticks_quote_string" || + typedObj["terminated"]["type"] === "hex_string" || + typedObj["terminated"]["type"] === "full_hex_string" || + typedObj["terminated"]["type"] === "natural_string" || + typedObj["terminated"]["type"] === "bit_string" || + typedObj["terminated"]["type"] === "double_quote_string" || + typedObj["terminated"]["type"] === "single_quote_string" || + typedObj["terminated"]["type"] === "bool" || + typedObj["terminated"]["type"] === "null" || + typedObj["terminated"]["type"] === "star" || + typedObj["terminated"]["type"] === "param" || + typedObj["terminated"]["type"] === "origin" || + typedObj["terminated"]["type"] === "date" || + typedObj["terminated"]["type"] === "datetime" || + typedObj["terminated"]["type"] === "default" || + typedObj["terminated"]["type"] === "time" || + typedObj["terminated"]["type"] === "timestamp") && + (typeof typedObj["terminated"]["value"] === "string" || + typeof typedObj["terminated"]["value"] === "number" || + typedObj["terminated"]["value"] === false || + typedObj["terminated"]["value"] === true) && + (typedObj["terminated"] !== null && + typeof typedObj["terminated"] === "object" || + typeof typedObj["terminated"] === "function") && + typeof typedObj["terminated"]["prefix"] === "string") && + (typedObj["enclosed"] === null || + (typedObj["enclosed"] !== null && + typeof typedObj["enclosed"] === "object" || + typeof typedObj["enclosed"] === "function") && + (typedObj["enclosed"]["type"] === "string" || + typedObj["enclosed"]["type"] === "number" || + typedObj["enclosed"]["type"] === "boolean" || + typedObj["enclosed"]["type"] === "backticks_quote_string" || + typedObj["enclosed"]["type"] === "hex_string" || + typedObj["enclosed"]["type"] === "full_hex_string" || + typedObj["enclosed"]["type"] === "natural_string" || + typedObj["enclosed"]["type"] === "bit_string" || + typedObj["enclosed"]["type"] === "double_quote_string" || + typedObj["enclosed"]["type"] === "single_quote_string" || + typedObj["enclosed"]["type"] === "bool" || + typedObj["enclosed"]["type"] === "null" || + typedObj["enclosed"]["type"] === "star" || + typedObj["enclosed"]["type"] === "param" || + typedObj["enclosed"]["type"] === "origin" || + typedObj["enclosed"]["type"] === "date" || + typedObj["enclosed"]["type"] === "datetime" || + typedObj["enclosed"]["type"] === "default" || + typedObj["enclosed"]["type"] === "time" || + typedObj["enclosed"]["type"] === "timestamp") && + (typeof typedObj["enclosed"]["value"] === "string" || + typeof typedObj["enclosed"]["value"] === "number" || + typedObj["enclosed"]["value"] === false || + typedObj["enclosed"]["value"] === true) && + (typedObj["enclosed"] !== null && + typeof typedObj["enclosed"] === "object" || + typeof typedObj["enclosed"] === "function") && + typeof typedObj["enclosed"]["prefix"] === "string") && + (typedObj["escaped"] === null || + (typedObj["escaped"] !== null && + typeof typedObj["escaped"] === "object" || + typeof typedObj["escaped"] === "function") && + (typedObj["escaped"]["type"] === "string" || + typedObj["escaped"]["type"] === "number" || + typedObj["escaped"]["type"] === "boolean" || + typedObj["escaped"]["type"] === "backticks_quote_string" || + typedObj["escaped"]["type"] === "hex_string" || + typedObj["escaped"]["type"] === "full_hex_string" || + typedObj["escaped"]["type"] === "natural_string" || + typedObj["escaped"]["type"] === "bit_string" || + typedObj["escaped"]["type"] === "double_quote_string" || + typedObj["escaped"]["type"] === "single_quote_string" || + typedObj["escaped"]["type"] === "bool" || + typedObj["escaped"]["type"] === "null" || + typedObj["escaped"]["type"] === "star" || + typedObj["escaped"]["type"] === "param" || + typedObj["escaped"]["type"] === "origin" || + typedObj["escaped"]["type"] === "date" || + typedObj["escaped"]["type"] === "datetime" || + typedObj["escaped"]["type"] === "default" || + typedObj["escaped"]["type"] === "time" || + typedObj["escaped"]["type"] === "timestamp") && + (typeof typedObj["escaped"]["value"] === "string" || + typeof typedObj["escaped"]["value"] === "number" || + typedObj["escaped"]["value"] === false || + typedObj["escaped"]["value"] === true) && + (typedObj["escaped"] !== null && + typeof typedObj["escaped"] === "object" || + typeof typedObj["escaped"] === "function") && + typeof typedObj["escaped"]["prefix"] === "string") + ) +} + +export function isLoadDataLine(obj: unknown): obj is LoadDataLine { + const typedObj = obj as LoadDataLine + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["keyword"] === "LINES" && + (typeof typedObj["starting"] === "undefined" || + (typedObj["starting"] !== null && + typeof typedObj["starting"] === "object" || + typeof typedObj["starting"] === "function") && + (typedObj["starting"]["type"] === "string" || + typedObj["starting"]["type"] === "number" || + typedObj["starting"]["type"] === "boolean" || + typedObj["starting"]["type"] === "backticks_quote_string" || + typedObj["starting"]["type"] === "hex_string" || + typedObj["starting"]["type"] === "full_hex_string" || + typedObj["starting"]["type"] === "natural_string" || + typedObj["starting"]["type"] === "bit_string" || + typedObj["starting"]["type"] === "double_quote_string" || + typedObj["starting"]["type"] === "single_quote_string" || + typedObj["starting"]["type"] === "bool" || + typedObj["starting"]["type"] === "null" || + typedObj["starting"]["type"] === "star" || + typedObj["starting"]["type"] === "param" || + typedObj["starting"]["type"] === "origin" || + typedObj["starting"]["type"] === "date" || + typedObj["starting"]["type"] === "datetime" || + typedObj["starting"]["type"] === "default" || + typedObj["starting"]["type"] === "time" || + typedObj["starting"]["type"] === "timestamp") && + (typeof typedObj["starting"]["value"] === "string" || + typeof typedObj["starting"]["value"] === "number" || + typedObj["starting"]["value"] === false || + typedObj["starting"]["value"] === true) && + (typedObj["starting"] !== null && + typeof typedObj["starting"] === "object" || + typeof typedObj["starting"] === "function") && + typeof typedObj["starting"]["prefix"] === "string") && + (typedObj["terminated"] === null || + (typedObj["terminated"] !== null && + typeof typedObj["terminated"] === "object" || + typeof typedObj["terminated"] === "function") && + (typedObj["terminated"]["type"] === "string" || + typedObj["terminated"]["type"] === "number" || + typedObj["terminated"]["type"] === "boolean" || + typedObj["terminated"]["type"] === "backticks_quote_string" || + typedObj["terminated"]["type"] === "hex_string" || + typedObj["terminated"]["type"] === "full_hex_string" || + typedObj["terminated"]["type"] === "natural_string" || + typedObj["terminated"]["type"] === "bit_string" || + typedObj["terminated"]["type"] === "double_quote_string" || + typedObj["terminated"]["type"] === "single_quote_string" || + typedObj["terminated"]["type"] === "bool" || + typedObj["terminated"]["type"] === "null" || + typedObj["terminated"]["type"] === "star" || + typedObj["terminated"]["type"] === "param" || + typedObj["terminated"]["type"] === "origin" || + typedObj["terminated"]["type"] === "date" || + typedObj["terminated"]["type"] === "datetime" || + typedObj["terminated"]["type"] === "default" || + typedObj["terminated"]["type"] === "time" || + typedObj["terminated"]["type"] === "timestamp") && + (typeof typedObj["terminated"]["value"] === "string" || + typeof typedObj["terminated"]["value"] === "number" || + typedObj["terminated"]["value"] === false || + typedObj["terminated"]["value"] === true) && + (typedObj["terminated"] !== null && + typeof typedObj["terminated"] === "object" || + typeof typedObj["terminated"] === "function") && + typeof typedObj["terminated"]["prefix"] === "string") + ) +} + +export function isTruncate(obj: unknown): obj is Truncate { + const typedObj = obj as Truncate + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "truncate" && + typedObj["keyword"] === "table" && + Array.isArray(typedObj["name"]) && + typedObj["name"].every((e: any) => + isFrom(e) as boolean + ) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isRename(obj: unknown): obj is Rename { + const typedObj = obj as Rename + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "rename" && + Array.isArray(typedObj["table"]) && + typedObj["table"].every((e: any) => + Array.isArray(e) && + (e[0] !== null && + typeof e[0] === "object" || + typeof e[0] === "function") && + (e[0]["db"] === null || + typeof e[0]["db"] === "string") && + typeof e[0]["table"] === "string" && + (e[1] !== null && + typeof e[1] === "object" || + typeof e[1] === "function") && + (e[1]["db"] === null || + typeof e[1]["db"] === "string") && + typeof e[1]["table"] === "string" + ) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isTransaction(obj: unknown): obj is Transaction { + const typedObj = obj as Transaction + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + typedObj["type"] === "transaction" && + (typedObj["expr"] !== null && + typeof typedObj["expr"] === "object" || + typeof typedObj["expr"] === "function") && + (typedObj["expr"]["action"] !== null && + typeof typedObj["expr"]["action"] === "object" || + typeof typedObj["expr"]["action"] === "function") && + (typedObj["expr"]["action"]["type"] === "string" || + typedObj["expr"]["action"]["type"] === "number" || + typedObj["expr"]["action"]["type"] === "boolean" || + typedObj["expr"]["action"]["type"] === "backticks_quote_string" || + typedObj["expr"]["action"]["type"] === "hex_string" || + typedObj["expr"]["action"]["type"] === "full_hex_string" || + typedObj["expr"]["action"]["type"] === "natural_string" || + typedObj["expr"]["action"]["type"] === "bit_string" || + typedObj["expr"]["action"]["type"] === "double_quote_string" || + typedObj["expr"]["action"]["type"] === "single_quote_string" || + typedObj["expr"]["action"]["type"] === "bool" || + typedObj["expr"]["action"]["type"] === "null" || + typedObj["expr"]["action"]["type"] === "star" || + typedObj["expr"]["action"]["type"] === "param" || + typedObj["expr"]["action"]["type"] === "origin" || + typedObj["expr"]["action"]["type"] === "date" || + typedObj["expr"]["action"]["type"] === "datetime" || + typedObj["expr"]["action"]["type"] === "default" || + typedObj["expr"]["action"]["type"] === "time" || + typedObj["expr"]["action"]["type"] === "timestamp") && + (typedObj["expr"]["action"]["value"] === "start" || + typedObj["expr"]["action"]["value"] === "begin" || + typedObj["expr"]["action"]["value"] === "commit" || + typedObj["expr"]["action"]["value"] === "rollback" || + typedObj["expr"]["action"]["value"] === "START" || + typedObj["expr"]["action"]["value"] === "COMMIT" || + typedObj["expr"]["action"]["value"] === "ROLLBACK") && + (typeof typedObj["expr"]["keyword"] === "undefined" || + typedObj["expr"]["keyword"] === "TRANSACTION") && + (typeof typedObj["expr"]["modes"] === "undefined" || + typedObj["expr"]["modes"] === null || + Array.isArray(typedObj["expr"]["modes"]) && + typedObj["expr"]["modes"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + (e["type"] === "string" || + e["type"] === "number" || + e["type"] === "boolean" || + e["type"] === "backticks_quote_string" || + e["type"] === "hex_string" || + e["type"] === "full_hex_string" || + e["type"] === "natural_string" || + e["type"] === "bit_string" || + e["type"] === "double_quote_string" || + e["type"] === "single_quote_string" || + e["type"] === "bool" || + e["type"] === "null" || + e["type"] === "star" || + e["type"] === "param" || + e["type"] === "origin" || + e["type"] === "date" || + e["type"] === "datetime" || + e["type"] === "default" || + e["type"] === "time" || + e["type"] === "timestamp") && + (typeof e["value"] === "string" || + typeof e["value"] === "number" || + e["value"] === false || + e["value"] === true) + )) && + (typeof typedObj["loc"] === "undefined" || + (typedObj["loc"] !== null && + typeof typedObj["loc"] === "object" || + typeof typedObj["loc"] === "function") && + (typedObj["loc"]["start"] !== null && + typeof typedObj["loc"]["start"] === "object" || + typeof typedObj["loc"]["start"] === "function") && + typeof typedObj["loc"]["start"]["line"] === "number" && + typeof typedObj["loc"]["start"]["column"] === "number" && + typeof typedObj["loc"]["start"]["offset"] === "number" && + (typedObj["loc"]["end"] !== null && + typeof typedObj["loc"]["end"] === "object" || + typeof typedObj["loc"]["end"] === "function") && + typeof typedObj["loc"]["end"]["line"] === "number" && + typeof typedObj["loc"]["end"]["column"] === "number" && + typeof typedObj["loc"]["end"]["offset"] === "number") + ) +} + +export function isAST(obj: unknown): obj is AST { + const typedObj = obj as AST + return ( + (isSelect(typedObj) as boolean || + isInsert_Replace(typedObj) as boolean || + isUpdate(typedObj) as boolean || + isDelete(typedObj) as boolean || + isAlter(typedObj) as boolean || + isUse(typedObj) as boolean || + isCreateTable(typedObj) as boolean || + isCreateDatabase(typedObj) as boolean || + isCreateSchema(typedObj) as boolean || + isCreateIndex(typedObj) as boolean || + isCreateView(typedObj) as boolean || + isCreateTrigger(typedObj) as boolean || + isCreateUser(typedObj) as boolean || + isDropTable(typedObj) as boolean || + isDropDatabase(typedObj) as boolean || + isDropView(typedObj) as boolean || + isDropIndex(typedObj) as boolean || + isDropTrigger(typedObj) as boolean || + isShow(typedObj) as boolean || + isDesc(typedObj) as boolean || + isExplain(typedObj) as boolean || + isCall(typedObj) as boolean || + isSet(typedObj) as boolean || + isLock(typedObj) as boolean || + isUnlock(typedObj) as boolean || + isGrant(typedObj) as boolean || + isLoadData(typedObj) as boolean || + isTruncate(typedObj) as boolean || + isRename(typedObj) as boolean || + isTransaction(typedObj) as boolean) + ) +} diff --git a/test/types/mysql/uncovered-types.spec.ts b/test/types/mysql/uncovered-types.spec.ts new file mode 100644 index 00000000..facfbcea --- /dev/null +++ b/test/types/mysql/uncovered-types.spec.ts @@ -0,0 +1,156 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Transaction, LoadData, Create, CreateColumnDefinition } from '../../types.d.ts'; +import { isTransaction, isLoadData, isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('Transaction with isolation level', () => { + const sql = 'START TRANSACTION ISOLATION LEVEL READ COMMITTED'; + const ast = parser.astify(sql); + + assert.ok(isTransaction(ast), 'Should be Transaction type'); + const txAst = ast as Transaction; + assert.strictEqual(txAst.type, 'transaction'); + assert.ok(txAst.expr.modes, 'Should have modes'); + assert.ok(Array.isArray(txAst.expr.modes), 'modes should be array'); + assert.strictEqual(txAst.expr.modes.length, 1); + // Note: Parser returns this as a single ValueExpr, not TransactionIsolationLevel object + assert.strictEqual(txAst.expr.modes[0].type, 'origin'); + assert.strictEqual(txAst.expr.modes[0].value, 'isolation level read committed'); +}); + +test('LoadData with FIELDS options', () => { + const sql = "LOAD DATA INFILE '/tmp/data.csv' INTO TABLE users FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\\\'"; + const ast = parser.astify(sql); + + assert.ok(isLoadData(ast), 'Should be LoadData type'); + const loadAst = ast as LoadData; + assert.strictEqual(loadAst.type, 'load_data'); + assert.ok(loadAst.fields, 'Should have fields'); + assert.strictEqual(loadAst.fields.keyword, 'FIELDS'); + assert.ok(loadAst.fields.terminated, 'Should have terminated'); + assert.strictEqual(loadAst.fields.terminated.value, ','); + assert.ok(loadAst.fields.enclosed, 'Should have enclosed'); + assert.strictEqual(loadAst.fields.enclosed.value, '"'); + assert.ok(loadAst.fields.escaped, 'Should have escaped'); + assert.strictEqual(loadAst.fields.escaped.value, '\\\\'); +}); + +test('LoadData with LINES options', () => { + const sql = "LOAD DATA INFILE '/tmp/data.csv' INTO TABLE users LINES STARTING BY 'xxx' TERMINATED BY '\\n'"; + const ast = parser.astify(sql); + + assert.ok(isLoadData(ast), 'Should be LoadData type'); + const loadAst = ast as LoadData; + assert.strictEqual(loadAst.type, 'load_data'); + assert.ok(loadAst.lines, 'Should have lines'); + assert.strictEqual(loadAst.lines.keyword, 'LINES'); + assert.ok(loadAst.lines.starting, 'Should have starting'); + assert.strictEqual(loadAst.lines.starting.value, 'xxx'); + assert.ok(loadAst.lines.terminated, 'Should have terminated'); + assert.strictEqual(loadAst.lines.terminated.value, '\\n'); +}); + +test('CREATE USER with REQUIRE SSL', () => { + const sql = "CREATE USER 'user'@'localhost' REQUIRE SSL"; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create'); + assert.strictEqual(ast.keyword, 'user'); + assert.ok(ast.require, 'Should have require'); + assert.strictEqual(ast.require.keyword, 'require'); + assert.ok(ast.require.value, 'Should have require value'); + assert.strictEqual(ast.require.value.type, 'origin'); + assert.strictEqual(ast.require.value.value, 'SSL'); +}); + +test('CREATE USER with resource options', () => { + const sql = "CREATE USER 'user'@'localhost' WITH MAX_QUERIES_PER_HOUR 100"; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create'); + assert.strictEqual(ast.keyword, 'user'); + assert.ok(ast.resource_options, 'Should have resource_options'); + assert.strictEqual(ast.resource_options.keyword, 'with'); + assert.ok(Array.isArray(ast.resource_options.value), 'resource_options.value should be array'); + assert.strictEqual(ast.resource_options.value[0].prefix, 'max_queries_per_hour'); + assert.strictEqual(ast.resource_options.value[0].value, 100); +}); + +test('CREATE TABLE with ENGINE option', () => { + const sql = "CREATE TABLE users (id INT) ENGINE=InnoDB"; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create'); + assert.strictEqual(ast.keyword, 'table'); + assert.ok(ast.table_options, 'Should have table_options'); + assert.ok(Array.isArray(ast.table_options), 'table_options should be array'); + assert.strictEqual(ast.table_options[0].keyword, 'engine'); + assert.strictEqual(ast.table_options[0].symbol, '='); + assert.strictEqual(ast.table_options[0].value, 'INNODB'); +}); + +test('Column with GENERATED ALWAYS AS', () => { + const sql = "CREATE TABLE users (full_name VARCHAR(100) GENERATED ALWAYS AS (CONCAT(first_name, ' ', last_name)) STORED)"; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create'); + assert.strictEqual(ast.keyword, 'table'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + const colDef = ast.create_definitions[0] as CreateColumnDefinition; + assert.ok(colDef.generated, 'Should have generated'); + assert.strictEqual(colDef.generated.type, 'generated'); + assert.ok(colDef.generated.expr, 'Should have generated expr'); + assert.strictEqual(colDef.generated.value, 'generated always as'); + assert.strictEqual(colDef.generated.storage_type, 'stored'); +}); + +test('Column with CHARACTER SET', () => { + const sql = "CREATE TABLE users (name VARCHAR(100) CHARACTER SET utf8mb4)"; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create'); + assert.strictEqual(ast.keyword, 'table'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + const colDef = ast.create_definitions[0] as CreateColumnDefinition; + assert.ok(colDef.character_set, 'Should have character_set'); + assert.strictEqual(colDef.character_set.type, 'CHARACTER SET'); + assert.ok(colDef.character_set.value, 'Should have character_set value'); + assert.strictEqual(colDef.character_set.value.type, 'default'); + assert.strictEqual(colDef.character_set.value.value, 'utf8mb4'); +}); + +test('Column with COLUMN_FORMAT', () => { + const sql = "CREATE TABLE users (id INT COLUMN_FORMAT FIXED)"; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create'); + assert.strictEqual(ast.keyword, 'table'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + const colDef = ast.create_definitions[0] as CreateColumnDefinition; + assert.ok(colDef.column_format, 'Should have column_format'); + assert.strictEqual(colDef.column_format.type, 'column_format'); + assert.strictEqual(colDef.column_format.value, 'fixed'); +}); + +test('Column with STORAGE', () => { + const sql = "CREATE TABLE users (id INT STORAGE DISK)"; + const ast = parser.astify(sql); + assert.ok(isCreate(ast), 'Should be Create'); + + assert.strictEqual(ast.type, 'create'); + assert.strictEqual(ast.keyword, 'table'); + assert.ok(ast.create_definitions, 'Should have create_definitions'); + const colDef = ast.create_definitions[0] as CreateColumnDefinition; + assert.ok(colDef.storage, 'Should have storage'); + assert.strictEqual(colDef.storage.type, 'storage'); + assert.strictEqual(colDef.storage.value, 'disk'); +}); diff --git a/test/types/mysql/union-set-ops.spec.ts b/test/types/mysql/union-set-ops.spec.ts new file mode 100644 index 00000000..99fe154d --- /dev/null +++ b/test/types/mysql/union-set-ops.spec.ts @@ -0,0 +1,50 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select } from '../../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('UNION - basic', () => { + const sql = 'SELECT id FROM users UNION SELECT id FROM customers'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select._next); + assert.ok(isSelect(select._next)); + assert.strictEqual(select.set_op, 'union'); +}); + +test('UNION ALL', () => { + const sql = 'SELECT id FROM users UNION ALL SELECT id FROM customers'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select._next); + assert.ok(isSelect(select._next)); + assert.strictEqual(select.set_op, 'union all'); +}); + +test('UNION DISTINCT', () => { + const sql = 'SELECT id FROM users UNION DISTINCT SELECT id FROM customers'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select._next); + assert.ok(isSelect(select._next)); + assert.ok(select.set_op); + assert.ok(select.set_op.includes('union')); +}); + +test('Multiple UNION operations', () => { + const sql = 'SELECT id FROM users UNION SELECT id FROM customers UNION SELECT id FROM vendors'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const select = ast as Select; + assert.ok(select._next); + assert.ok(isSelect(select._next)); + const next = select._next as Select; + assert.ok(next._next); + assert.ok(isSelect(next._next)); +}); diff --git a/test/types/mysql/update-properties.spec.ts b/test/types/mysql/update-properties.spec.ts new file mode 100644 index 00000000..51891fed --- /dev/null +++ b/test/types/mysql/update-properties.spec.ts @@ -0,0 +1,166 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Update, From, Dual, Binary, Unary, Function as FunctionType } from '../../../types.d.ts'; +import { isUpdate, isFrom, isDual, isBinary, isUnary, isFunction } from './types.guard.ts'; + +const parser = new Parser(); + +// Test Update.with - With[] variant +test('Update.with as With[]', () => { + const sql = 'WITH cte AS (SELECT id FROM temp) UPDATE users SET name = "John" WHERE id IN (SELECT id FROM cte)'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.ok(Array.isArray(update.with)); + assert.strictEqual(update.with!.length, 1); + assert.strictEqual(update.with![0].name.value, 'cte'); +}); + +// Test Update.with - null variant +test('Update.with as null', () => { + const sql = 'UPDATE users SET name = "John"'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.strictEqual(update.with, null); +}); + +// Test Update.table - Array variant with From +test('Update.table as Array', () => { + const sql = 'UPDATE users SET name = "John"'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.ok(Array.isArray(update.table)); + assert.strictEqual(update.table!.length, 1); + assert.ok(isFrom(update.table![0])); + const from = update.table![0] as From; + assert.strictEqual(from.table, 'users'); +}); + +// Test Update.table - Array with multiple tables +test('Update.table with multiple tables (JOIN)', () => { + const sql = 'UPDATE users u JOIN orders o ON u.id = o.user_id SET u.name = "John"'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.ok(Array.isArray(update.table)); + assert.ok(update.table!.length >= 1); +}); + +// Test Update.table - null variant +test('Update.table as null', () => { + const sql = 'UPDATE users SET name = "John"'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + // Table should be an array, not null in MySQL + assert.ok(update.table !== null); +}); + +// Test Update.set - SetList[] +test('Update.set as SetList[]', () => { + const sql = 'UPDATE users SET name = "John", age = 30, email = "john@example.com"'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.ok(Array.isArray(update.set)); + assert.strictEqual(update.set.length, 3); + assert.strictEqual(update.set[0].column, 'name'); + assert.strictEqual(update.set[1].column, 'age'); + assert.strictEqual(update.set[2].column, 'email'); +}); + +// Test Update.where - Binary variant +test('Update.where as Binary', () => { + const sql = 'UPDATE users SET name = "John" WHERE id = 1'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.ok(update.where); + assert.ok(isBinary(update.where)); + const binary = update.where as Binary; + assert.strictEqual(binary.type, 'binary_expr'); +}); + +// Test Update.where - Unary variant +test('Update.where as Unary', () => { + const sql = 'UPDATE users SET name = "John" WHERE NOT active'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.ok(update.where); + if (update.where && 'type' in update.where && update.where.type === 'unary_expr') { + assert.ok(isUnary(update.where)); + } +}); + +// Test Update.where - Function variant +test('Update.where as Function', () => { + const sql = 'UPDATE users SET name = "John" WHERE ISNULL(deleted_at)'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.ok(update.where); + if (update.where && 'type' in update.where && update.where.type === 'function') { + assert.ok(isFunction(update.where)); + } +}); + +// Test Update.where - null variant +test('Update.where as null', () => { + const sql = 'UPDATE users SET name = "John"'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.strictEqual(update.where, null); +}); + +// Test Update.orderby - OrderBy[] variant +test('Update.orderby as OrderBy[]', () => { + const sql = 'UPDATE users SET name = "John" ORDER BY id DESC'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.ok(Array.isArray(update.orderby)); + assert.strictEqual(update.orderby!.length, 1); + assert.strictEqual(update.orderby![0].type, 'DESC'); +}); + +// Test Update.orderby - null variant +test('Update.orderby as null', () => { + const sql = 'UPDATE users SET name = "John"'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.strictEqual(update.orderby, null); +}); + +// Test Update.limit - Limit variant +test('Update.limit as Limit', () => { + const sql = 'UPDATE users SET name = "John" LIMIT 10'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.ok(update.limit); + assert.ok(Array.isArray(update.limit!.value)); +}); + +// Test Update.limit - null variant +test('Update.limit as null', () => { + const sql = 'UPDATE users SET name = "John"'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.strictEqual(update.limit, null); +}); + +// Test Update.type +test('Update.type as "update"', () => { + const sql = 'UPDATE users SET name = "John"'; + const ast = parser.astify(sql); + assert.ok(isUpdate(ast)); + const update = ast as Update; + assert.strictEqual(update.type, 'update'); +}); diff --git a/test/types/mysql/update.spec.ts b/test/types/mysql/update.spec.ts new file mode 100644 index 00000000..d808f311 --- /dev/null +++ b/test/types/mysql/update.spec.ts @@ -0,0 +1,32 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Update, SetList } from '../../types.d.ts'; +import { isUpdate } from './types.guard.ts'; + +const parser = new Parser(); + +test('UPDATE with SET', () => { + const sql = "UPDATE users SET name = 'Jane' WHERE id = 1"; + const ast = parser.astify(sql); + + assert.ok(isUpdate(ast), 'AST should be an Update type'); + const updateAst = ast as Update; + assert.strictEqual(updateAst.type, 'update'); + assert.ok(Array.isArray(updateAst.set)); + const setList = updateAst.set as SetList[]; + assert.strictEqual(setList[0].column, 'name'); + assert.ok(updateAst.where); +}); + +test('UPDATE multiple columns', () => { + const sql = "UPDATE users SET name = 'Jane', age = 30 WHERE id = 1"; + const ast = parser.astify(sql); + + assert.ok(isUpdate(ast), 'AST should be an Update type'); + const updateAst = ast as Update; + assert.strictEqual(updateAst.type, 'update'); + assert.strictEqual(updateAst.set.length, 2); + assert.strictEqual(updateAst.set[0].column, 'name'); + assert.strictEqual(updateAst.set[1].column, 'age'); +}); diff --git a/test/types/mysql/utility-types.spec.ts b/test/types/mysql/utility-types.spec.ts new file mode 100644 index 00000000..6561bb15 --- /dev/null +++ b/test/types/mysql/utility-types.spec.ts @@ -0,0 +1,50 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Interval, Param, Var, TableColumnAst, ParseOptions, Option } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('Interval - DATE_ADD with INTERVAL', () => { + const sql = 'SELECT DATE_ADD(NOW(), INTERVAL 1 DAY)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); +}); + +test('Param - prepared statement parameter', () => { + const sql = 'SELECT * FROM users WHERE id = :id'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); +}); + +test('Var - variable reference', () => { + const sql = 'SELECT @myvar'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); +}); + +test('TableColumnAst - parse result', () => { + const sql = 'SELECT * FROM users'; + const ast = parser.astify(sql) as TableColumnAst; + assert.ok(isSelect(ast), 'ast should be a Select type'); +}); + +test('ParseOptions - with includeLocations', () => { + const sql = 'SELECT * FROM users'; + const options: ParseOptions = { includeLocations: true }; + const ast = parser.astify(sql, { parseOptions: options }); + + assert.ok(isSelect(ast), 'AST should be a Select type'); +}); + +test('Option - with database and parseOptions', () => { + const sql = 'SELECT * FROM users'; + const option: Option = { database: 'MySQL', parseOptions: { includeLocations: true } }; + const ast = parser.astify(sql, option); + + assert.ok(isSelect(ast), 'AST should be a Select type'); +}); diff --git a/test/types/mysql/value-expr-types.spec.ts b/test/types/mysql/value-expr-types.spec.ts new file mode 100644 index 00000000..a16a7285 --- /dev/null +++ b/test/types/mysql/value-expr-types.spec.ts @@ -0,0 +1,148 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, ValueExpr } from '../../types.d.ts'; +import { isSelect, isCreate } from './types.guard.ts'; + +const parser = new Parser(); + +test('ValueExpr - string type', () => { + const sql = "SELECT 'hello'"; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.strictEqual(expr.type, 'single_quote_string'); + assert.strictEqual(expr.value, 'hello'); +}); + +test('ValueExpr - number type', () => { + const sql = 'SELECT 42'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.strictEqual(expr.type, 'number'); + assert.strictEqual(expr.value, 42); +}); + +test('ValueExpr - boolean type (TRUE)', () => { + const sql = 'SELECT TRUE'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.ok(expr.type === 'bool' || expr.type === 'boolean'); + assert.strictEqual(expr.value, true); +}); + +test('ValueExpr - boolean type (FALSE)', () => { + const sql = 'SELECT FALSE'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.ok(expr.type === 'bool' || expr.type === 'boolean'); + assert.strictEqual(expr.value, false); +}); + +test('ValueExpr - null type', () => { + const sql = 'SELECT NULL'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.strictEqual(expr.type, 'null'); + assert.strictEqual(expr.value, null); +}); + +test('ValueExpr - double_quote_string type', () => { + const sql = 'SELECT "hello"'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.strictEqual(expr.type, 'double_quote_string'); + assert.strictEqual(expr.value, 'hello'); +}); + +test('ValueExpr - backticks_quote_string type', () => { + const sql = 'SELECT `hello`'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr; + // Backticks are typically used for identifiers, not values + // This might be a column reference instead + assert.ok(expr); +}); + +test('ValueExpr - hex_string type', () => { + const sql = "SELECT X'4D7953514C'"; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.ok(expr.type === 'hex_string' || expr.type === 'full_hex_string'); +}); + +test('ValueExpr - bit_string type', () => { + const sql = "SELECT B'1010'"; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.strictEqual(expr.type, 'bit_string'); +}); + +test('ValueExpr - date type', () => { + const sql = "SELECT DATE '2023-01-01'"; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.strictEqual(expr.type, 'date'); +}); + +test('ValueExpr - datetime type', () => { + const sql = "SELECT TIMESTAMP '2023-01-01 12:00:00'"; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.ok(expr.type === 'datetime' || expr.type === 'timestamp'); +}); + +test('ValueExpr - time type', () => { + const sql = "SELECT TIME '12:00:00'"; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.strictEqual(expr.type, 'time'); +}); + +test('ValueExpr - default type', () => { + const sql = 'CREATE TABLE t (id INT DEFAULT 0)'; + const ast = parser.astify(sql); + assert.ok(isCreate(ast)); +}); + +test('ValueExpr - param type', () => { + const sql = 'SELECT :param'; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr; + assert.ok(expr.type === 'param' || expr.type === 'var'); +}); + +test('ValueExpr - natural_string type (N prefix)', () => { + const sql = "SELECT N'hello'"; + const ast = parser.astify(sql); + assert.ok(isSelect(ast)); + const col = (ast as Select).columns[0]; + const expr = col.expr as ValueExpr; + assert.strictEqual(expr.type, 'natural_string'); + assert.strictEqual(expr.value, 'hello'); +}); diff --git a/test/types/mysql/window-functions.spec.ts b/test/types/mysql/window-functions.spec.ts new file mode 100644 index 00000000..7fbf5ca6 --- /dev/null +++ b/test/types/mysql/window-functions.spec.ts @@ -0,0 +1,29 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Column, AggrFunc, WindowSpec, WindowFrameClause } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('Window function with frame clause', () => { + const sql = 'SELECT SUM(amount) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM orders'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + const col = (selectAst.columns as Column[])[0]; + const aggrFunc = col.expr as AggrFunc; + assert.strictEqual(aggrFunc.type, 'aggr_func'); + + // Window functions have over property + const over = aggrFunc.over; + assert.ok(over === undefined || typeof over === 'object'); + + // WindowFrameClause type is defined in types.d.ts + // The actual structure may vary, but the types should compile + if (over && typeof over === 'object' && 'window_specification' in over) { + const spec = (over as any).window_specification as WindowSpec; + assert.ok(spec === undefined || typeof spec === 'object'); + } +}); diff --git a/test/types/mysql/window-types.spec.ts b/test/types/mysql/window-types.spec.ts new file mode 100644 index 00000000..6eb3d6c5 --- /dev/null +++ b/test/types/mysql/window-types.spec.ts @@ -0,0 +1,92 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, Column, AggrFunc, WindowSpec, AsWindowSpec, WindowExpr, NamedWindowExpr } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('Window function - OVER with PARTITION BY', () => { + const sql = 'SELECT SUM(amount) OVER (PARTITION BY user_id ORDER BY date) FROM orders'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const col = (ast.columns as Column[])[0]; + const aggrFunc = col.expr as AggrFunc; + assert.ok(aggrFunc.over); + assert.strictEqual('type' in aggrFunc.over, true, 'type should be present'); + assert.strictEqual('as_window_specification' in aggrFunc.over, true, 'as_window_specification should be present'); +}); + +test('Window function - OVER with window name', () => { + const sql = 'SELECT SUM(amount) OVER w FROM orders WINDOW w AS (PARTITION BY user_id)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const col = (ast.columns as Column[])[0]; + const aggrFunc = col.expr as AggrFunc; + assert.ok(aggrFunc.over); +}); + +test('WindowSpec - check properties', () => { + const sql = 'SELECT SUM(amount) OVER (PARTITION BY user_id ORDER BY date) FROM orders'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const col = (ast.columns as Column[])[0]; + const aggrFunc = col.expr as AggrFunc; + + if (aggrFunc.over && typeof aggrFunc.over === 'object' && 'as_window_specification' in aggrFunc.over) { + const asSpec = aggrFunc.over.as_window_specification as any; + if (typeof asSpec === 'object' && 'window_specification' in asSpec) { + const spec = asSpec.window_specification as WindowSpec; + assert.strictEqual('name' in spec, true, 'name should be present'); + assert.strictEqual('partitionby' in spec, true, 'partitionby should be present'); + assert.strictEqual('orderby' in spec, true, 'orderby should be present'); + assert.strictEqual('window_frame_clause' in spec, true, 'window_frame_clause should be present'); + } + } +}); + +test('WindowExpr - WINDOW clause', () => { + const sql = 'SELECT SUM(amount) OVER w FROM orders WINDOW w AS (PARTITION BY user_id)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + if (ast.window) { + assert.strictEqual('keyword' in ast.window, true, 'keyword should be present'); + assert.strictEqual('type' in ast.window, true, 'type should be present'); + assert.strictEqual('expr' in ast.window, true, 'expr should be present'); + } +}); + +test('NamedWindowExpr - check properties', () => { + const sql = 'SELECT SUM(amount) OVER w FROM orders WINDOW w AS (PARTITION BY user_id)'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + if (ast.window) { + const namedExpr = ast.window.expr[0] as NamedWindowExpr; + assert.strictEqual('name' in namedExpr, true, 'name should be present'); + assert.strictEqual('as_window_specification' in namedExpr, true, 'as_window_specification should be present'); + } +}); + +test('Window function - with frame clause', () => { + const sql = 'SELECT SUM(amount) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM orders'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const col = (ast.columns as Column[])[0]; + const aggrFunc = col.expr as AggrFunc; + + if (aggrFunc.over && typeof aggrFunc.over === 'object' && 'as_window_specification' in aggrFunc.over) { + const asSpec = aggrFunc.over.as_window_specification as any; + if (typeof asSpec === 'object' && 'window_specification' in asSpec) { + const spec = asSpec.window_specification as WindowSpec; + if (spec.window_frame_clause) { + assert.strictEqual('type' in spec.window_frame_clause, true, 'window_frame_clause should have type'); + } + } + } +}); diff --git a/test/types/mysql/with-clause.spec.ts b/test/types/mysql/with-clause.spec.ts new file mode 100644 index 00000000..4213485b --- /dev/null +++ b/test/types/mysql/with-clause.spec.ts @@ -0,0 +1,32 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Parser } from './parser-loader.mjs'; +import type { Select, With, ColumnRef } from '../../types.d.ts'; +import { isSelect } from './types.guard.ts'; + +const parser = new Parser(); + +test('WITH clause columns type', () => { + const sql = 'WITH cte (id, name) AS (SELECT id, name FROM users) SELECT * FROM cte'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const selectAst = ast as Select; + assert.strictEqual(selectAst.type, 'select'); + assert.ok(Array.isArray(selectAst.with)); + const withClause = selectAst.with![0] as With; + assert.ok(Array.isArray(withClause.columns)); + const col = withClause.columns![0] as ColumnRef; + assert.ok(col); +}); + +test('WITH clause without columns', () => { + const sql = 'WITH cte AS (SELECT id, name FROM users) SELECT * FROM cte'; + const ast = parser.astify(sql); + + assert.ok(isSelect(ast), 'AST should be a Select type'); + const withClause = ast.with![0] as With; + assert.strictEqual('columns' in withClause, true, 'columns should be present'); + assert.strictEqual('name' in withClause, true, 'name should be present'); + assert.strictEqual('stmt' in withClause, true, 'stmt should be present'); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..bccd8217 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "noEmit": true + }, + "ts-auto-guard": { + "export-guards": true, + "guard-name": "is%s", + "path-mode": "relative" + }, + "include": ["types.d.ts"], + "exclude": ["node_modules", "test", "lib", "build"] +} diff --git a/types.d.ts b/types.d.ts index 6034c37d..d96a364d 100644 --- a/types.d.ts +++ b/types.d.ts @@ -12,13 +12,12 @@ export interface With { columnList: string[]; ast: Select; }; - columns?: any[]; + columns: ColumnRef[] | null; } import { LocationRange } from "pegjs"; export { LocationRange, Location } from "pegjs"; -export type WhilteListCheckMode = "table" | "column"; export interface ParseOptions { includeLocations?: boolean; } @@ -32,6 +31,7 @@ export interface TableColumnAst { tableList: string[]; columnList: string[]; ast: AST[] | AST; + parentheses?: boolean; loc?: LocationRange; } export interface BaseFrom { @@ -39,6 +39,7 @@ export interface BaseFrom { table: string; as: string | null; schema?: string; + addition?: boolean; loc?: LocationRange; } export interface Join extends BaseFrom { @@ -48,10 +49,12 @@ export interface Join extends BaseFrom { } export interface TableExpr { expr: { + tableList: string[]; + columnList: string[]; ast: Select; + parentheses: boolean; }; - as?: string | null; - parentheses: boolean | { length: number } + as: string | null; } export interface Dual { type: "dual"; @@ -69,8 +72,8 @@ export interface Limit { loc?: LocationRange; } export interface OrderBy { - type: "ASC" | "DESC"; - expr: any; + type: "ASC" | "DESC" | null; + expr: ExpressionValue; loc?: LocationRange; } @@ -78,7 +81,7 @@ export interface ValueExpr { type: | "backticks_quote_string" | "string" - | "regex_string" + | "number" | "hex_string" | "full_hex_string" | "natural_string" @@ -95,8 +98,7 @@ export interface ValueExpr { | "datetime" | "default" | "time" - | "timestamp" - | "var_string"; + | "timestamp"; value: T; } @@ -104,30 +106,24 @@ export type SortDirection = 'ASC' | 'DESC' | 'asc' | 'desc'; export interface ColumnRefItem { type: "column_ref"; - table: string | null; + table?: string | null; column: string | { expr: ValueExpr }; options?: ExprList; loc?: LocationRange; - collate?: { collate: CollateExpr } | null; + collate?: CollateExpr | null; order_by?: SortDirection | null; } -export interface ColumnRefExpr { - type: "expr"; - expr: ColumnRefItem; - as: string | null; -} - -export type ColumnRef = ColumnRefItem | ColumnRefExpr; +export type ColumnRef = ColumnRefItem; export interface SetList { column: string; - value: any; + value: ExpressionValue; table: string | null; loc?: LocationRange; } export interface InsertReplaceValue { type: "expr_list"; - value: any[]; - prefix?: string; + value: ExpressionValue[]; + prefix: string | null; loc?: LocationRange; } @@ -166,11 +162,13 @@ export interface AggrFunc { name: string; args: { expr: ExpressionValue; - distinct: "DISTINCT" | null; - orderby: OrderBy[] | null; + distinct?: "DISTINCT" | null; + orderby?: OrderBy[] | null; parentheses?: boolean; + separator?: { keyword: string; value: Value } | string | null; }; loc?: LocationRange; + over: { type: 'window'; as_window_specification: AsWindowSpec } | null; } export type FunctionName = { @@ -181,7 +179,8 @@ export interface Function { type: "function"; name: FunctionName; args?: ExprList; - suffix?: any; + suffix?: OnUpdateCurrentTimestamp | null; + over?: { type: 'window'; as_window_specification: AsWindowSpec } | null; loc?: LocationRange; } export interface Column { @@ -199,7 +198,9 @@ export interface Interval { export type Param = { type: "param"; value: string; loc?: LocationRange }; -export type Value = { type: string; value: any; loc?: LocationRange }; +export type Var = { type: "var"; name: string; members: string[]; prefix: string; loc?: LocationRange }; + +export type Value = { type: string; value: string | number | boolean | null; loc?: LocationRange }; export type Binary = { type: "binary_expr"; @@ -210,37 +211,49 @@ export type Binary = { parentheses?: boolean; }; -export type Expr = Binary; +export type Unary = { + type: "unary_expr"; + operator: string; + expr: ExpressionValue; + loc?: LocationRange; + parentheses?: boolean; +}; + +export type Expr = Binary | Unary; export type ExpressionValue = | ColumnRef | Param + | Var | Function | Case | AggrFunc | Value | Binary + | Unary | Cast - | Interval; + | Interval + | Star + | TableColumnAst; export type ExprList = { type: "expr_list"; - value: ExpressionValue[]; + value: ExpressionValue[] | null; loc?: LocationRange; parentheses?: boolean; separator?: string; }; -export type PartitionBy = { - type: 'expr'; - expr: ColumnRef[]; -}[]; +export type PartitionBy = Column[]; export type WindowSpec = { - name: null; - partitionby: PartitionBy; + name: string | null; + partitionby: PartitionBy | null; orderby: OrderBy[] | null; - window_frame_clause: string | null; }; + window_frame_clause: WindowFrameClause | null; +}; + +export type WindowFrameClause = Binary; export type AsWindowSpec = string | { window_specification: WindowSpec; parentheses: boolean }; @@ -258,17 +271,22 @@ export type WindowExpr = { export interface Select { with: With[] | null; type: "select"; - options: any[] | null; + options: ("SQL_CALC_FOUND_ROWS" | "SQL_CACHE" | "SQL_NO_CACHE" | "SQL_SMALL_RESULT" | "SQL_BIG_RESULT" | "SQL_BUFFER_RESULT")[] | null; distinct: "DISTINCT" | null; - columns: any[] | Column[]; - from: From[] | TableExpr | null ; - where: Binary | Function | null; - groupby: { columns: ColumnRef[] | null, modifiers: ValueExpr[] }; - having: any[] | null; + columns: Column[]; + into: { + keyword?: string; + type?: string; + expr?: Var[] | Value; + position: 'column' | 'from' | 'end' | null; + }; + from: From[] | TableExpr | { expr: From[], parentheses: { length: number }, joins: From[] } | null; + where: Binary | Unary | Function | null; + groupby: { columns: ColumnRef[] | null, modifiers: (ValueExpr | null)[] } | null; + having: Binary | null; orderby: OrderBy[] | null; limit: Limit | null; - window?: WindowExpr; - qualify?: any[] | null; + window: WindowExpr | null; _orderby?: OrderBy[] | null; _limit?: Limit | null; parentheses_symbol?: boolean; @@ -276,75 +294,241 @@ export interface Select { loc?: LocationRange; _next?: Select; set_op?: string; + collate: CollateExpr | null; + locking_read: string | null; } export interface Insert_Replace { type: "replace" | "insert"; - table: any; + table: From[] | From; columns: string[] | null; - values: { + values?: { type: 'values', values: InsertReplaceValue[] } | Select; - partition: any[]; - prefix: string; + set?: SetList[]; + partition: string[] | null; + prefix: "" | "ignore" | "into" | "ignore into"; on_duplicate_update: { keyword: "on duplicate key update", set: SetList[]; - }; + } | null; loc?: LocationRange; - returning?: Returning -} -export interface Returning { - type: 'returning'; - columns: ColumnRef | Select; } export interface Update { + with: With[] | null; type: "update"; - db: string | null; table: Array | null; set: SetList[]; - where: Binary | Function | null; + where: Binary | Unary | Function | null; + orderby: OrderBy[] | null; + limit: Limit | null; loc?: LocationRange; - returning?: Returning } export interface Delete { + with: With[] | null; type: "delete"; - table: any; + table: (From & { addition?: boolean })[] | null; from: Array; - where: Binary | Function | null; + where: Binary | Unary | Function | null; + orderby: OrderBy[] | null; + limit: Limit | null; loc?: LocationRange; - returning?: Returning } export interface Alter { type: "alter"; - table: From[]; - expr: any; + table: Array<{ db: string | null; table: string }>; + expr: AlterExpr[]; loc?: LocationRange; } +export type AlterExpr = + | AlterAddColumn + | AlterDropColumn + | AlterModifyColumn + | AlterChangeColumn + | AlterRenameTable + | AlterRenameColumn + | AlterAddIndex + | AlterDropIndex + | AlterDropKey + | AlterAddConstraint + | AlterDropConstraint + | AlterAddPartition + | AlterDropPartition + | AlterAlgorithm + | AlterLock + | AlterTableOption; + +export type AlterAddColumn = { + type: 'alter'; + resource: 'column'; + action: 'add'; + keyword?: 'COLUMN'; + column: ColumnRef; + definition: DataType; + suffix: string | null | { keyword: string }; +}; + +export type AlterDropColumn = { + type: 'alter'; + resource: 'column'; + action: 'drop'; + keyword?: 'COLUMN'; + column: ColumnRef; +}; + +export type AlterModifyColumn = { + type: 'alter'; + resource: 'column'; + action: 'modify'; + keyword: 'COLUMN' | null; + column: ColumnRef; + definition: DataType; + suffix: string | null | { keyword: string }; +}; + +export type AlterChangeColumn = { + type: 'alter'; + resource: 'column'; + action: 'change'; + keyword: 'COLUMN' | null; + old_column: ColumnRef; + column: ColumnRef; + definition: DataType; + suffix: string | null | { keyword: string }; +}; + +export type AlterRenameTable = { + type: 'alter'; + resource: 'table'; + action: 'rename'; + keyword: string; + table: string; +}; + +export type AlterRenameColumn = { + type: 'alter'; + resource: 'column'; + action: 'rename'; + keyword: 'column'; + old_column: ColumnRef; + prefix: string; + column: ColumnRef; +}; + +export type AlterAddIndex = { + type: 'alter'; + resource: 'index'; + action: 'add'; + keyword: string; + index: string; + definition: ColumnRef[]; + index_type: IndexType | null; + index_options: IndexOption[] | null; +}; + +export type AlterDropIndex = { + type: 'alter'; + resource: 'index'; + action: 'drop'; + keyword: string; + index: string; +}; + +export type AlterDropKey = { + type: 'alter'; + resource: 'key'; + action: 'drop'; + keyword: string; + key: string; +}; + +export type AlterAddConstraint = { + type: 'alter'; + resource: 'constraint'; + action: 'add'; + create_definitions: CreateConstraintDefinition; +}; + +export type AlterDropConstraint = { + type: 'alter'; + resource: 'constraint'; + action: 'drop'; + keyword: string; + constraint: string; +}; + +export type AlterAddPartition = { + type: 'alter'; + resource: 'partition'; + action: 'add'; + keyword: 'PARTITION'; + partitions: Array<{ + name: ValueExpr; + value: { + type: string; + expr: Value; + parentheses: boolean; + }; + }>; +}; + +export type AlterDropPartition = { + type: 'alter'; + resource: 'partition'; + action: 'drop'; + keyword: 'PARTITION'; + partitions: Column[]; +}; + +export type AlterAlgorithm = { + type: 'alter'; + resource: 'algorithm'; + keyword: 'algorithm'; + symbol: string; + algorithm: string; +}; + +export type AlterLock = { + type: 'alter'; + resource: 'lock'; + keyword: 'lock'; + symbol: string; + lock: string; +}; + +export type AlterTableOption = { + type: 'alter'; + resource: string; + keyword: string; + symbol: string; + engine?: string; +}; + export interface Use { type: "use"; db: string; loc?: LocationRange; } -export type KW_UNSIGNED = "UNSIGNED"; -export type KW_ZEROFILL = "ZEROFILL"; - -export type Timezone = ["WITHOUT" | "WITH", "TIME", "ZONE"]; - export type KeywordComment = { type: "comment"; keyword: "comment"; - symbol?: "="; - value: string; + symbol?: "=" | null; + value: ValueExpr | string; }; export type CollateExpr = { type: "collate"; - symbol?: "="; - value: string; + keyword?: "collate"; + symbol?: "=" | null; + value?: string; + collate?: { + name: string; + symbol: "=" | null; + }; + name?: string; }; export type DataType = { @@ -352,9 +536,15 @@ export type DataType = { length?: number; parentheses?: true; scale?: number; - suffix?: Timezone | (KW_UNSIGNED | KW_ZEROFILL)[]; - array?: "one" | "two"; + suffix?: ("UNSIGNED" | "ZEROFILL")[] | OnUpdateCurrentTimestamp | null; expr?: Expr | ExprList; + quoted?: string; +}; + +export type OnUpdateCurrentTimestamp = { + type: 'on_update_current_timestamp'; + keyword: 'on update'; + expr: Function; }; export type LiteralNotNull = { @@ -363,12 +553,11 @@ export type LiteralNotNull = { }; export type LiteralNull = { type: "null"; value: null | "null" }; -export type LiteralNumeric = number | { type: "bigint"; value: string }; export type ColumnConstraint = { default_val: { type: "default"; - value: any; + value: ExpressionValue; }; nullable: LiteralNotNull | LiteralNull; }; @@ -378,13 +567,41 @@ export type ColumnDefinitionOptList = { default_val?: ColumnConstraint["default_val"]; auto_increment?: "auto_increment"; unique?: "unique" | "unique key"; - primary?: "key" | "primary key"; + primary_key?: "key" | "primary key"; comment?: KeywordComment; - collate?: { collate: CollateExpr }; - column_format?: { column_format: any }; - storage?: { storage: any }; - reference_definition?: { reference_definition: any }; - character_set?: { type: "CHARACTER SET"; value: string; symbol?: "=" }; + collate?: CollateExpr; + column_format?: { type: string; value: string }; + storage?: { type: string; value: string }; + reference_definition?: ReferenceDefinition; + character_set?: { type: "CHARACTER SET"; value: ValueExpr; symbol: "=" | null }; + check?: { + constraint_type: 'check'; + keyword: 'constraint' | null; + constraint: string | null; + definition: ExpressionValue[]; + enforced: 'enforced' | 'not enforced' | ''; + resource: 'constraint'; + }; + generated?: { + type: 'generated'; + expr: ExpressionValue; + value: string; + storage_type?: 'stored' | 'virtual'; + }; +}; + +export type ReferenceDefinition = { + definition?: ColumnRef[]; + table?: From[]; + keyword?: string; + match?: string | null; + on_action: OnReference[]; +}; + +export type OnReference = { + type: 'on update' | 'on delete'; + keyword?: 'on delete' | 'on update'; + value: 'restrict' | 'cascade' | 'set null' | 'no action' | 'set default' | ValueExpr; }; export type CreateColumnDefinition = { @@ -401,20 +618,29 @@ export type IndexType = { export type IndexOption = { type: "key_block_size"; symbol?: "="; - expr: LiteralNumeric; -}; + expr: Value; +} | { + keyword: "using"; + type: "btree" | "hash"; +} | { + type: "with parser"; + expr: string; +} | { + type: "visible" | "invisible"; + expr: string; +} | KeywordComment; export type CreateIndexDefinition = { - index?: string; + index: string | null; definition: ColumnRef[]; keyword: "index" | "key"; - index_type?: IndexType; + index_type: IndexType | null; resource: "index"; - index_options?: IndexOption[]; + index_options: IndexOption[] | null; }; export type CreateFulltextSpatialIndexDefinition = { - index?: string; + index?: string | null; definition: ColumnRef[]; keyword?: | "fulltext" @@ -423,49 +649,50 @@ export type CreateFulltextSpatialIndexDefinition = { | "spatial key" | "fulltext index" | "spatial index"; - index_options?: IndexOption[]; + index_options?: IndexOption[] | null; resource: "index"; }; export type ConstraintName = { keyword: "constraint"; constraint: string }; export type CreateConstraintPrimary = { - constraint?: ConstraintName["constraint"]; + constraint?: ConstraintName["constraint"] | null; definition: ColumnRef[]; constraint_type: "primary key"; - keyword?: ConstraintName["keyword"]; - index_type?: IndexType; + keyword?: ConstraintName["keyword"] | null; + index_type?: IndexType | null; resource: "constraint"; - index_options?: IndexOption[]; + index_options?: IndexOption[] | null; }; export type CreateConstraintUnique = { - constraint?: ConstraintName["constraint"]; + constraint?: ConstraintName["constraint"] | null; definition: ColumnRef[]; constraint_type: "unique key" | "unique" | "unique index"; - keyword?: ConstraintName["keyword"]; - index_type?: IndexType; - index?: string; + keyword?: ConstraintName["keyword"] | null; + index_type?: IndexType | null; + index?: string | null; resource: "constraint"; - index_options?: IndexOption[]; + index_options?: IndexOption[] | null; }; export type CreateConstraintForeign = { - constraint?: ConstraintName["constraint"]; + constraint?: ConstraintName["constraint"] | null; definition: ColumnRef[]; - constraint_type: "FOREIGN KEY"; - keyword?: ConstraintName["keyword"]; - index?: string; + constraint_type: "foreign key" | "FOREIGN KEY"; + keyword?: ConstraintName["keyword"] | null; + index?: string | null; resource: "constraint"; - reference_definition?: any; + reference_definition?: ReferenceDefinition; }; export type CreateConstraintCheck = { - constraint?: ConstraintName["constraint"]; - definition: any[]; + constraint?: ConstraintName["constraint"] | null; + definition: Binary[]; constraint_type: "check"; - keyword?: ConstraintName["keyword"]; + keyword?: ConstraintName["keyword"] | null; resource: "constraint"; + index_type?: IndexType | null; }; export type CreateConstraintDefinition = @@ -480,54 +707,346 @@ export type CreateDefinition = | CreateFulltextSpatialIndexDefinition | CreateConstraintDefinition; -export interface Create { +export interface CreateTable { type: "create"; - keyword: "aggregate" | "table" | "trigger" | "extension" | "function" | "index" | "database" | "schema" | "view" | "domain" | "type" | "user"; - temporary?: "temporary" | null; - table?: { db: string; table: string }[] | { db: string | null, table: string }; - if_not_exists?: "if not exists" | null; + keyword: "table"; + temporary: "temporary" | null; + table: { db: string | null; table: string }[] | { db: string | null, table: string }; + if_not_exists: "IF NOT EXISTS" | null; like?: { type: "like"; - table: string; + table: From[]; parentheses?: boolean; } | null; ignore_replace?: "ignore" | "replace" | null; as?: string | null; - query_expr?: any | null; + query_expr?: Select | null; create_definitions?: CreateDefinition[] | null; - table_options?: any[] | null; + table_options?: TableOption[] | null; + loc?: LocationRange; +} + +export interface CreateDatabase { + type: "create"; + keyword: "database"; + if_not_exists?: "IF NOT EXISTS" | null; + database?: string | { schema: ValueExpr[] }; + loc?: LocationRange; +} + +export interface CreateSchema { + type: "create"; + keyword: "schema"; + if_not_exists?: "IF NOT EXISTS" | null; + database?: string | { schema: ValueExpr[] }; + loc?: LocationRange; +} + +export interface CreateIndex { + type: "create"; + keyword: "index"; index_using?: { keyword: "using"; type: "btree" | "hash"; } | null; - index?: string | null | { schema: string | null, name: string}; + index?: string | null; on_kw?: "on" | null; + table?: { db: string | null; table: string }[] | { db: string | null, table: string }; index_columns?: ColumnRefItem[] | null; index_type?: "unique" | "fulltext" | "spatial" | null; - index_options?: any[] | null; + index_options?: IndexOption[] | null; algorithm_option?: { type: "alter"; keyword: "algorithm"; resource: "algorithm"; symbol: "=" | null; - algorithm: "default" | "instant" | "inplace" | "copy"; + algorithm: string; } | null; lock_option?: { type: "alter"; keyword: "lock"; resource: "lock"; symbol: "=" | null; - lock: "default" | "none" | "shared" | "exclusive"; + lock: string; } | null; - database?: string; loc?: LocationRange; - where?: Binary | Function | null } -export interface Drop { +export interface CreateView { + type: "create"; + keyword: "view"; + replace: "or replace" | null; + algorithm: "UNDEFINED" | "MERGE" | "TEMPTABLE" | null; + definer: Binary | null; + sql_security: "DEFINER" | "INVOKER" | null; + view: { db: string | null; view: string }; + columns: string[] | null; + select: Select; + with: "with check option" | "with cascaded check option" | "with local check option" | null; + loc?: LocationRange; +} + +export interface CreateTrigger { + type: "create"; + keyword: "trigger"; + definer: Binary | null; + trigger: { db: string | null; table: string }; + time: string; + events: TriggerEvent[]; + table: { db: string | null; table: string }; + for_each: { keyword: string; args: string }; + order: { + keyword: 'FOLLOWS' | 'PRECEDES'; + trigger: string; + } | null; + execute: { type: "set"; expr: SetList[] }; + if_not_exists: string | null; + loc?: LocationRange; +} + +export interface CreateUser { + type: "create"; + keyword: "user"; + if_not_exists?: "IF NOT EXISTS" | null; + user?: UserAuthOption[] | null; + default_role?: string[] | null; + require?: RequireOption | null; + resource_options?: ResourceOption | null; + password_options?: PasswordOption | null; + lock_option_user?: 'account lock' | 'account unlock' | null; + comment_user?: string | null; + attribute?: string | null; + loc?: LocationRange; +} + +export type Create = CreateTable | CreateDatabase | CreateSchema | CreateIndex | CreateView | CreateTrigger | CreateUser; + +export type TriggerEvent = { + keyword: 'insert' | 'update' | 'delete'; + args?: ColumnRef[]; +}; + +export type UserAuthOption = { + user: { + name: ValueExpr; + host: ValueExpr; + }; + auth_option?: { + keyword: string; + auth_plugin?: string | null; + value: ValueExpr & { prefix?: string }; + } | null; +}; + +export type RequireOption = { + keyword: 'require'; + value: ValueExpr; +}; + +export type ResourceOption = { + keyword: 'with'; + value: Array<{ + type: string; + value: number; + prefix: string; + }>; +}; + +export type PasswordOption = { + type: 'password_expire' | 'password_history' | 'password_reuse_interval' | 'password_require_current'; + value: number | string | null; +}; + +export type TableOption = { + keyword: string; + symbol?: '='; + value: ExpressionValue | string | number; +}; + +export interface DropTable { + type: "drop"; + keyword: "table"; + name: From[]; + prefix: 'if exists' | null; + loc?: LocationRange; +} + +export interface DropDatabase { type: "drop"; + keyword: "database" | "schema"; + name: string; + prefix: 'if exists' | null; + loc?: LocationRange; +} + +export interface DropView { + type: "drop"; + keyword: "view"; + name: From[]; + prefix: 'if exists' | null; + options: 'restrict' | 'cascade' | null; + loc?: LocationRange; +} + +export interface DropIndex { + type: "drop"; + keyword: "index"; + name: ColumnRef; + table: From; + options: 'restrict' | 'cascade' | null; + loc?: LocationRange; +} + +export interface DropTrigger { + type: "drop"; + keyword: "trigger"; + name: Array<{ schema: string | null; trigger: string }>; + prefix: 'if exists' | null; + loc?: LocationRange; +} + +export type Drop = DropTable | DropDatabase | DropView | DropIndex | DropTrigger; + +export interface Show { + type: "show"; keyword: string; - name: any[]; + suffix?: string; + from?: From; + where?: Binary | Function | null; + like?: { + type: 'like'; + value: string; + } | null; + loc?: LocationRange; +} + +export interface Desc { + type: "desc"; + table: string; + loc?: LocationRange; +} + +export interface Explain { + type: "explain"; + expr: Select | Update | Delete | Insert_Replace; + format?: string; + loc?: LocationRange; +} + +export interface Call { + type: "call"; + expr: Function; + loc?: LocationRange; +} + +export interface Set { + type: "set"; + keyword?: string | null; + expr: Array<{ + type: "assign"; + left: Var; + symbol: string; + right: ExpressionValue; + }>; + loc?: LocationRange; +} + +export interface Lock { + type: "lock"; + keyword: "tables"; + tables: LockTable[]; + loc?: LocationRange; +} + +export type LockTable = { + table: From; + lock_type: { + type: 'read' | 'write'; + suffix?: null; + prefix?: null; + }; +}; + +export interface Unlock { + type: "unlock"; + keyword: "tables"; + loc?: LocationRange; +} + +export interface Grant { + type: "grant"; + keyword: "priv"; + objects: Array<{ + priv: ValueExpr; + columns: ColumnRef[] | null; + }>; + on: { + object_type: 'table' | 'function' | 'procedure' | null; + priv_level: Array<{ + prefix: string; + name: string; + }>; + }; + to_from: "TO" | "FROM"; + user_or_roles: Array<{ + name: ValueExpr; + host: ValueExpr | null; + }>; + with: any | null; + loc?: LocationRange; +} + +export interface LoadData { + type: "load_data"; + mode?: string | null; + local?: 'local' | null; + file: ValueExpr; + replace_ignore?: 'replace' | 'ignore' | null; + table: { db: string | null; table: string }; + partition?: ValueExpr[] | null; + character_set?: string | null; + fields?: LoadDataField | null; + lines?: LoadDataLine | null; + ignore?: number | null; + column?: ColumnRef[] | null; + set?: SetList[] | null; + loc?: LocationRange; +} + +export type LoadDataField = { + keyword: 'FIELDS'; + terminated: ValueExpr & { prefix: string } | null; + enclosed: ValueExpr & { prefix: string } | null; + escaped: ValueExpr & { prefix: string } | null; +}; + +export type LoadDataLine = { + keyword: 'LINES'; + starting?: ValueExpr & { prefix: string }; + terminated: ValueExpr & { prefix: string } | null; +}; + +export interface Truncate { + type: "truncate"; + keyword: "table"; + name: From[]; + loc?: LocationRange; +} + +export interface Rename { + type: "rename"; + table: Array<[{ db: string | null; table: string }, { db: string | null; table: string }]>; + loc?: LocationRange; +} + +export interface Transaction { + type: "transaction"; + expr: { + action: ValueExpr<"start" | "begin" | "commit" | "rollback" | "START" | "COMMIT" | "ROLLBACK">; + keyword?: "TRANSACTION"; + modes?: ValueExpr[] | null; + }; + loc?: LocationRange; } export type AST = @@ -538,7 +1057,19 @@ export type AST = | Delete | Alter | Create - | Drop; + | Drop + | Show + | Desc + | Explain + | Call + | Set + | Lock + | Unlock + | Grant + | LoadData + | Truncate + | Rename + | Transaction; export class Parser { constructor(); @@ -549,7 +1080,7 @@ export class Parser { sqlify(ast: AST[] | AST, opt?: Option): string; - exprToSQL(ast: any, opt?: Option): string; + exprToSQL(ast: ExpressionValue | ExprList | OrderBy | ColumnRef, opt?: Option): string; whiteListCheck( sql: string,