{"id":15003439,"url":"https://github.com/jeremiah-shaulov/polysql","last_synced_at":"2026-02-23T03:59:33.905Z","repository":{"id":57675551,"uuid":"401094098","full_name":"jeremiah-shaulov/polysql","owner":"jeremiah-shaulov","description":"Queries generator for MySQL, PostgreSQL, SQLite and Microsoft SQL Server","archived":false,"fork":false,"pushed_at":"2025-08-15T09:56:14.000Z","size":543,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-11-09T23:09:41.987Z","etag":null,"topics":["deno","microsoft-sql-server","mysql","postgresql","sql-generation","sqlite3"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jeremiah-shaulov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-08-29T16:51:40.000Z","updated_at":"2025-08-15T09:55:06.000Z","dependencies_parsed_at":"2024-10-12T07:40:53.488Z","dependency_job_id":"1f687173-4233-408d-84b7-4c6b8da9a3a7","html_url":"https://github.com/jeremiah-shaulov/polysql","commit_stats":{"total_commits":18,"total_committers":2,"mean_commits":9.0,"dds":0.05555555555555558,"last_synced_commit":"f32b8f38bc0faee89c40e86fca8ddab80c45c3fa"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/jeremiah-shaulov/polysql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremiah-shaulov%2Fpolysql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremiah-shaulov%2Fpolysql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremiah-shaulov%2Fpolysql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremiah-shaulov%2Fpolysql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jeremiah-shaulov","download_url":"https://codeload.github.com/jeremiah-shaulov/polysql/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremiah-shaulov%2Fpolysql/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29736974,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-23T02:24:00.660Z","status":"ssl_error","status_checked_at":"2026-02-23T02:22:56.087Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deno","microsoft-sql-server","mysql","postgresql","sql-generation","sqlite3"],"created_at":"2024-09-24T18:58:30.481Z","updated_at":"2026-02-23T03:59:33.898Z","avatar_url":"https://github.com/jeremiah-shaulov.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\n\tThis file is generated with the following command:\n\tdeno run --allow-all https://raw.githubusercontent.com/jeremiah-shaulov/tsa/v0.0.57/tsa.ts doc-md --outFile=README.md --outUrl=https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md --importUrl=https://deno.land/x/polysql@v2.0.18/mod.ts mod.ts\n--\u003e\n\n# polysql\n\n[Documentation Index](generated-doc/README.md)\n\nThis library assists developers to generate SQL queries for MySQL, PostgreSQL, SQLite and Microsoft SQL Server.\nIt's designed for those who's interested in utilizing the true power of relational databases (not a \"no-SQL\" SQL).\nIt tries to make queries safe, and migration to different database engine easier.\n\nThis library can:\n\n- Quote SQL literals (string, blob, date, ...)\n- Form certain parts in an SQL query, like names-values in INSERT, to generate \"WHERE\" expressions, etc.\n- Generate SELECT, INSERT, UPDATE, DELETE and TRUNCATE queries from parts\n\n## Quote SQL literals\n\nThis library provides the following functions:\n\n\u003e `function` [mysqlQuote](generated-doc/function.mysqlQuote/README.md)(value: `unknown`, noBackslashEscapes: `boolean`=false): `string`\u003cbr\u003e\n\u003e `function` [pgsqlQuote](generated-doc/function.pgsqlQuote/README.md)(value: `unknown`, \\_unused: `boolean`=false): `string`\u003cbr\u003e\n\u003e `function` [sqliteQuote](generated-doc/function.sqliteQuote/README.md)(value: `unknown`, \\_unused: `boolean`=false): `string`\u003cbr\u003e\n\u003e `function` [mssqlQuote](generated-doc/function.mssqlQuote/README.md)(value: `unknown`, \\_unused: `boolean`=false): `string`\n\nUsually you need to import only one of these functions into your project.\n\n```ts\nimport {mysqlQuote as sqlQuote} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconsole.log(sqlQuote(import.meta.url));\n```\nFunction `mysqlQuote()` has second parameter called `noBackslashEscapes`.\nIf it's true, backslashes in SQL string literals will be assumed not to have special meaning, so `mysqlQuote()` will not double backslashes.\nIt's important to provide the correct value to this parameter.\nRemember that the value of this parameter can change during server session, if user executes a query like `SET sql_mode='no_backslash_escapes'`.\n\nThe \"value\" parameter can be one of the following types:\n\n- null, undefined, Javascript functions and Symbol objects produce `NULL` literal\n- boolean produces `FALSE` or `TRUE` literals (`0` or `1` for Microsoft SQL Server)\n- number and bigint is printed as is\n- Date produces string like `2021-08-26` or `2021-08-26 10:00:00` or `2021-08-26 10:00:00.123`\n- typed arrays (like Uint8Array) produce literals like `x'00112233'` (`0x00112233` for Microsoft SQL Server)\n- Sql object will print a string with it's query\n- ReadableStream will be rejected with exception\n- other types will be converted to strings and printed as an SQL string literal\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-p9mn\u003e/' \u003e /tmp/example-p9mn.ts\n// deno run /tmp/example-p9mn.ts\n\nimport {mysqlQuote as sqlQuote} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconsole.log(sqlQuote(null)); // prints: NULL\nconsole.log(sqlQuote(false)); // prints: FALSE\nconsole.log(sqlQuote(123)); // prints: 123\nconsole.log(sqlQuote('Message')); // prints: 'Message'\nconsole.log(sqlQuote('It\\'s another message')); // prints: 'It''s another message'\nconsole.log(sqlQuote(new Date(2000, 0, 1))); // prints: '2000-01-01'\nconsole.log(sqlQuote(new Uint8Array([1, 2, 3]))); // prints: x'010203'\nconsole.log(sqlQuote({id: 1, value: 1.5})); // prints: '{\"id\":1,\"value\":1.5}'\n```\n\n## Produce parts of SQL queries\n\nThis library provides the following string-template functions:\n\n- mysql and mysqlOnly\n- pgsql and pgsqlOnly\n- sqlite and sqliteOnly\n- mssql and mssqlOnly\n\nUsually you need to import only one of these functions into your project.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-65ya\u003e/' \u003e /tmp/example-65ya.ts\n// deno run /tmp/example-65ya.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nlet message = `It's the message`;\nlet number = 0.1;\nlet column = 'The number';\nconsole.log('' + sql`SELECT '${message}', '${number}' AS \"${column}\"`); // prints: SELECT 'It''s the message', 0.1 AS `The number`\n```\n\n`*Only` allows you to use all the supported features for that SQL dialect, even those that are not supported for other dialects.\n\nTags without `*Only` throw exception if you ask a feature that is not supported by all of MySQL, PostgreSQL, Sqlite and Microsoft SQL Server. So you can switch to different dialect later (e.g. from `mysql` to `mssql`).\n\nYou can mark backtick-quoted Javascript strings with the `sql` tag, as in example above, and dollar-brace parameters in this string will be escaped.\n\nHow each parameter is escaped depends on quotes that you used in your SQL string, to quote this parameter (in the example above i quoted `message` and `number` with apostrophes, and `column` with double-quotes).\n\n### 1. `'${param}'` - Escape an SQL value.\n\nIf the parameter is a string, characters inside it will be properly escaped (if you use `mysql`, a mysqlNoBackslashEscapes argument of [Sql.toString()](generated-doc/class.Sql/README.md#-tostringputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-string) or [Sql.encode()](generated-doc/class.Sql/README.md#-encodeputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-usebuffer-uint8array-usebufferfrompos-number0-defaultparentname-uint8array-uint8arrayarraybufferlike) will be respected - see below).\n\nIf the value is a number, quotes around it will be removed.\n\nIf it's a `null`, or an `undefined`, a Javascript function or a Symbol, it will be substituted with `NULL` literal.\n\nIf it's boolean `false` or `true`, it will be substituted with `FALSE` or `TRUE` (`0` or `1` on Microsoft SQL Server).\n\n`Date` objects will be printed as SQL dates.\n\nTyped arrays will be printed like `x'0102...'` (`0x0102...` on Microsoft SQL Server).\n\n`ReadableStream` objects will be put to `putParamsTo` array, if it's provided to [Sql.toString()](generated-doc/class.Sql/README.md#-tostringputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-string) or [Sql.encode()](generated-doc/class.Sql/README.md#-encodeputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-usebuffer-uint8array-usebufferfrompos-number0-defaultparentname-uint8array-uint8arrayarraybufferlike) - see below, and the value will be replaced with '?' character.\nIf `putParamsTo` not provided, exception will be thrown.\n\nObjects will be JSON-stringified.\n\n### 2. `\"${param}\"` or `` \\`${param}\\` `` - Escape an identifier (column, table or routine name, etc.).\n\nFor MySQL double quotes will be replaced with backticks. For others, backticks (if you used them) will be converted to quotes.\n\nIdentifier cannot contain ASCII 0 characters (required for PostgreSQL).\n\n### 3. `\"${param}*\"`, `\"${param}+\"`, `\"${param},\"` - Escape a list of identifiers (also can use backticks instead of quotes).\n\nGenerates comma-separated list of quoted identifiers from iterable collection \"param\".\n\n`\"${param}*\"` - if the collection is empty, generates `*` character (as in `SELECT * FROM ...`).\n\n`\"${param}+\"` - throws exception if the collection is empty.\n\n`\"${param},\"` - doesn't generate any output, if the collection is empty. If it's not empty, prints a comma after the last identifier.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-pf4z\u003e/' \u003e /tmp/example-pf4z.ts\n// deno run /tmp/example-pf4z.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nlet noNames: never[] = [];\nlet names = ['one', 'two'];\n\nconsole.log('' + sql`SELECT \"${noNames}*\"`); // prints: SELECT *\nconsole.log('' + sql`SELECT \"${names}*\"`); // prints: SELECT `one`, `two`\nconsole.log('' + sql`SELECT \"${noNames},\" three`); // prints: SELECT  three\nconsole.log('' + sql`SELECT \"${names},\" three`); // prints: SELECT `one`, `two`, three\n```\n\n### 3b. `\"parent_name.${param}*\"`, `\"parent_name.${param}+\"`, `\"parent_name.${param},\"`\n\nThe same as [3], but qualifies each identifier with specified parent name.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-ksv8\u003e/' \u003e /tmp/example-ksv8.ts\n// deno run /tmp/example-ksv8.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nlet noNames: never[] = [];\nlet names = ['one', 'two'];\n\nconsole.log('' + sql`SELECT \"t1.${noNames}*\"`); // prints: SELECT *\nconsole.log('' + sql`SELECT \"t1.${names}*\"`); // prints: SELECT `t1`.`one`, `t1`.`two`\nconsole.log('' + sql`SELECT \"t1.${noNames},\" three`); // prints: SELECT  three\nconsole.log('' + sql`SELECT \"t1.${names},\" three`); // prints: SELECT `t1`.`one`, `t1`.`two`, three\n```\n\n### 4. `[${param}]` - Generate list of SQL values.\n\nSquare brackets will be replaced with parentheses. The parameter must be iterable.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-rlut\u003e/' \u003e /tmp/example-rlut.ts\n// deno run /tmp/example-rlut.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst ids = [10, 11, 12];\nlet s = sql`SELECT * FROM articles WHERE id IN [${ids}]`;\nconsole.log('' + s); // prints: SELECT * FROM articles WHERE id IN (10,11,12)\n```\n\nIf there are no items in the collection, it generates `(NULL)`.\n\nIf items in the collection are also iterable, this will generate multidimensional list.\n2-Dimensional lists are only supported by MySQL and PostgreSQL.\nMore than 2 dimensions are only supported by MySQL.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-ajdy\u003e/' \u003e /tmp/example-ajdy.ts\n// deno run /tmp/example-ajdy.ts\n\nimport {mysqlOnly as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst list = [[10, 1], [11, 3], [12, 8]];\nlet s = sql\n`\tSELECT *\n\tFROM articles AS a\n\tINNER JOIN article_versions AS av ON a.id = av.article_id\n\tWHERE (av.article_id, av.article_version) IN [${list}]\n`;\nconsole.log('' + s); // prints: ...WHERE (av.article_id, av.article_version) IN ((10,1),(11,3),(12,8))\n```\n\n### 5. `(${param})` or `(parent_name.${param})` - Embed a safe SQL expression.\n\nThe inserted SQL fragment will be validated, so it doesn't contain the following characters (unless quoted): `@ $ # ? : [ ] { } ;`, `\\0`-char, commas except in parentheses, comments, unterminated literals, unbalanced parentheses. Identifiers in this SQL fragment will be quoted according to chosen policy (see below).\n\nStrings in the SQL fragment are always treated as `mysqlNoBackslashEscapes` (backslash is regular character), so to represent a string with a new line, you need `const expr = \"Char_length('Line\\n')\"`, not `const expr = \"Char_length('Line\\\\n')\"`.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-7bvr\u003e/' \u003e /tmp/example-7bvr.ts\n// deno run /tmp/example-7bvr.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst expr = \"Char_length('Line\\n')\";\nlet s = sql`SELECT (${expr})`;\nconsole.log('' + s);\n```\n\nIt's possible to prefix all unqualified identifiers in the SQL fragment with a parent qualifier:\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-dbu0\u003e/' \u003e /tmp/example-dbu0.ts\n// deno run /tmp/example-dbu0.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst expr = \"article_id = 10 AND `article_version` = 1 AND a.name \u003c\u003e ''\";\nlet s = sql\n`\tSELECT a.name, av.*\n\tFROM articles AS a\n\tINNER JOIN article_versions AS av ON a.id = av.article_id\n\tWHERE (av.${expr})\n`;\nconsole.log('' + s); // prints ...WHERE (`av`.article_id = 10 AND `av`.`article_version` = 1 AND `a`.name \u003c\u003e '')\n```\n\n### 6. `${param}` or `parent_name.${param}` (not enclosed) - Like `(${param})`, but allows commas on top level.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-2dm1\u003e/' \u003e /tmp/example-2dm1.ts\n// deno run /tmp/example-2dm1.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst columns = \"name, value\";\nlet s = sql`SELECT ${columns} FROM something WHERE id=1`;\nconsole.log('' + s); // prints: SELECT `name`, `value` FROM something WHERE id=1\n```\n\n### 7. `{parent_name.${param}}`, `{parent_name.${param},}` - Generate equations separated with commas (the `parent_name` is optional).\n\nThe first form throws exception, if there are no fields in the param. The Second form doesn't complain, and prints comma after the last field.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-aymx\u003e/' \u003e /tmp/example-aymx.ts\n// deno run /tmp/example-aymx.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst row = {name: 'About all', author: 'Johnny'};\nlet s = sql`UPDATE articles AS a SET {a.${row}} WHERE id=1`;\nconsole.log('' + s); // prints: UPDATE articles AS a SET `a`.`name`='About all', `a`.`author`='Johnny' WHERE id=1\n```\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-10go\u003e/' \u003e /tmp/example-10go.ts\n// deno run /tmp/example-10go.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst row = {name: 'About all', author: 'Johnny'};\nlet s = sql`UPDATE articles AS a SET {a.${row},} article_date=Now() WHERE id=1`;\nconsole.log('' + s); // prints: UPDATE articles AS a SET `a`.`name`='About all', `a`.`author`='Johnny', article_date=Now() WHERE id=1\n```\n\nIf a value is an [Sql](generated-doc/class.Sql/README.md) object, it's expression will be used.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-1f17\u003e/' \u003e /tmp/example-1f17.ts\n// deno run /tmp/example-1f17.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst row = {name: 'About all', author: sql`Get_author(id)`};\nlet s = sql`UPDATE articles AS a SET {a.${row}} WHERE id=1`;\nconsole.log('' + s); // prints: UPDATE articles AS a SET `a`.`name`='About all', `a`.`author`=Get_author(`a`.id) WHERE id=1\n```\n\n### 8. `{parent_name.${param}\u0026}` - Generate equations separated with \"AND\" operations (the `parent_name` is optional).\n\nConverts braces to parentheses. If the `param` contains no fields, this will be converted to a `FALSE` literal.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-855m\u003e/' \u003e /tmp/example-855m.ts\n// deno run /tmp/example-855m.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst row = {name: 'About all', author: sql`Get_author(id)`};\nlet s = sql`SELECT * FROM articles AS a WHERE {a.${row}\u0026}`;\nconsole.log('' + s); // prints: SELECT * FROM articles AS a WHERE (`a`.`name`='About all' AND `a`.`author`=Get_author(`a`.id))\n```\n\n### 9. `{parent_name.${param}|}` - Generate equations separated with \"OR\" operations (the `parent_name` is optional).\n\nConverts braces to parentheses. If the `param` contains no fields, this will be converted to a `TRUE` literal.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-t6u5\u003e/' \u003e /tmp/example-t6u5.ts\n// deno run /tmp/example-t6u5.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst row = {name: 'About all', author: sql`Get_author(id)`};\nlet s = sql`SELECT * FROM articles AS a WHERE {a.${row}|}`;\nconsole.log('' + s); // prints: SELECT * FROM articles AS a WHERE (`a`.`name`='About all' OR `a`.`author`=Get_author(`a`.id))\n```\n\n### 10. `{left_parent_name.right_parent_name.${param}}`\n\nIn [7], [8] and [9], you can specify 2 parent qualifiers: one for the left-hand side of the equation, and one for the right. Any of the names can be empty.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-wr6k\u003e/' \u003e /tmp/example-wr6k.ts\n// deno run /tmp/example-wr6k.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst row = {name: 'About all', author: sql`Get_author(id)`};\nlet s = sql`SELECT * FROM articles AS a INNER JOIN article_content AS ac ON a.id = ac.article_id WHERE {a.ac.${row}\u0026}`;\nconsole.log('' + s); // prints: SELECT * FROM articles AS a INNER JOIN article_content AS ac ON a.id = ac.article_id WHERE (`a`.`name`='About all' AND `a`.`author`=Get_author(`ac`.id))\n```\n\nExample of left name empty:\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-np7j\u003e/' \u003e /tmp/example-np7j.ts\n// deno run /tmp/example-np7j.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst row = {name: 'About all', author: sql`Get_author(id)`};\nlet s = sql`UPDATE articles AS a SET {.a.${row}} WHERE id=1`;\nconsole.log('' + s); // prints: UPDATE articles AS a SET `name`='About all', `author`=Get_author(`a`.id) WHERE id=1\n```\n\n### 11. `\u003c${param}\u003e` - Generate names and values for INSERT statement.\n\nParameter must be iterable object that contains rows to insert. Will print column names from the first row. On following rows, only columns from the first row will be used.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-7xum\u003e/' \u003e /tmp/example-7xum.ts\n// deno run /tmp/example-7xum.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nlet rows =\n[\t{value: 10, name: 'text 1'},\n\t{value: 11, name: 'text 2'},\n];\nconsole.log('' + sql`INSERT INTO t_log \u003c${rows}\u003e AS excluded ON DUPLICATE KEY UPDATE t_log.name = excluded.name`);\n\n// prints:\n// INSERT INTO t_log (`value`, `name`) VALUES\n// (10,'text 1'),\n// (11,'text 2') AS excluded ON DUPLICATE KEY UPDATE t_log.name = excluded.name\n```\n\n### 12. `\"${parent_name}.${param}*\"`, `(${parent_name}.${param})`, `${parent_name}.${param}`, `{${parent_name}.${param}}` - Takes the `parent_name` from a variable.\n\nIn [3b], [5], [6], [7], [8], [9] and [10] the parent qualifier name can be taken from a variable.\n\n## About [Sql](generated-doc/class.Sql/README.md) object\n\n`sql` template function returns object of [Sql](generated-doc/class.Sql/README.md) class.\n\n```ts\nimport {mysql as sql, Sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nlet s: Sql = sql`SELECT 2*2`;\n```\n\n### [Sql.append()](generated-doc/class.Sql/README.md#-appendother-sql-this) and [Sql.concat()](generated-doc/class.Sql/README.md#-concatother-sql-sql)\n\n[Sql](generated-doc/class.Sql/README.md) objects can be appended to and concatenated:\n\n\u003e ⚙ Sql.[append](generated-doc/class.Sql/README.md#-appendother-sql-this)(other: [Sql](generated-doc/class.Sql/README.md)): `this`\u003cbr\u003e\n\u003e ⚙ Sql.[concat](generated-doc/class.Sql/README.md#-concatother-sql-sql)(other: [Sql](generated-doc/class.Sql/README.md)): [Sql](generated-doc/class.Sql/README.md)\n\n[append()](generated-doc/class.Sql/README.md#-appendother-sql-this) modifies current object, and [concat()](generated-doc/class.Sql/README.md#-concatother-sql-sql) returns a new one.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-h9zl\u003e/' \u003e /tmp/example-h9zl.ts\n// deno run /tmp/example-h9zl.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst id = 10;\nconst s = sql`SELECT * FROM articles WHERE id='${id}'`;\n\nconst where = `name \u003c\u003e ''`;\n\nconst s2 = s.concat(sql` AND (${where})`); // concat() returns a new object\nconsole.log('' + s2); // prints: SELECT * FROM articles WHERE id=10 AND (`name` \u003c\u003e '')\n\ns.append(sql` AND (${where})`); // append() modifies current object\nconsole.log('' + s); // prints the same\n```\n\n### [Sql.encode()](generated-doc/class.Sql/README.md#-encodeputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-usebuffer-uint8array-usebufferfrompos-number0-defaultparentname-uint8array-uint8arrayarraybufferlike)\n\nAlso [Sql](generated-doc/class.Sql/README.md) objects can be converted to bytes.\n\n\u003e ⚙ Sql.[encode](generated-doc/class.Sql/README.md#-encodeputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-usebuffer-uint8array-usebufferfrompos-number0-defaultparentname-uint8array-uint8arrayarraybufferlike)(putParamsTo?: `unknown`\\[], mysqlNoBackslashEscapes: `boolean`=false, useBuffer?: Uint8Array, useBufferFromPos: `number`=0, defaultParentName?: Uint8Array): Uint8Array\\\u003cArrayBufferLike\u003e\n\nThis function converts the SQL query to `Uint8Array`, that you probably need to pass to your SQL driver.\n\nYou can pass an array to the `putParamsTo` parameter, so long strings and long typed arrays, and `ReadableStream` objects, that appear in quoted `'${value}'` parameters, will be put to this array,\nand their SQL representation will be produced as `?` character.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-khw0\u003e/' \u003e /tmp/example-khw0.ts\n// deno run /tmp/example-khw0.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst message = 'a'.repeat(100);\nconst params: unknown[] = [];\nconst writer = Deno.stdout.writable.getWriter();\ntry\n{\tconst sqlBytes = sql`SELECT '${message}'`.encode(params);\n\n\tawait writer.write(sqlBytes); // prints: SELECT ?\n\tconsole.log('');\n\tconsole.log(params); // prints: ['aaa...']\n}\nfinally\n{\twriter.releaseLock();\n}\n```\n\nIf `putParamsTo` is not provided, `ReadableStream` objects will be rejected with exception.\n\nThe `mysqlNoBackslashEscapes` parameter is only respected when using MySQL dialect.\nIf it's true, backslashes in SQL string literals will be assumed not to have special meaning, so `` mysql`'${value}'` `` will not double backslashes.\nIt's important to provide the correct value to this parameter.\nRemember that the value of this parameter can change during server session, if user executes a query like `SET sql_mode='no_backslash_escapes'`.\n\nIf `useBuffer` parameter is provided, and there's enough space in this buffer, this buffer will be used and a `useBuffer.subarray()` of it will be returned from `sql.encode()`.\nIf it's not big enough, a new buffer will be allocated, as usual.\n\nIf `useBufferFromPos` parameter is provided together with the `useBuffer`, so the produced query will be appended after that position in the buffer, and the contents of `useBuffer` before this position will be the part of returned query (even if a new buffer was allocated).\n\n### [Sql.toString()](generated-doc/class.Sql/README.md#-tostringputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-string)\n\n[Sql](generated-doc/class.Sql/README.md) objects can be stringified.\n\n\u003e ⚙ Sql.[toString](generated-doc/class.Sql/README.md#-tostringputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-string)(putParamsTo?: `unknown`\\[], mysqlNoBackslashEscapes: `boolean`=false): `string`\n\nThis function calls [Sql.encode()](generated-doc/class.Sql/README.md#-encodeputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-usebuffer-uint8array-usebufferfrompos-number0-defaultparentname-uint8array-uint8arrayarraybufferlike), and then converts the result to string.\n\n### [Sql.toSqlBytesWithParamsBackslashAndBuffer()](generated-doc/class.Sql/README.md#-tosqlbyteswithparamsbackslashandbufferputparamsto-unknown--undefined-mysqlnobackslashescapes-boolean-usebuffer-uint8array-uint8arrayarraybufferlike)\n\n\u003e ⚙ Sql.[toSqlBytesWithParamsBackslashAndBuffer](generated-doc/class.Sql/README.md#-tosqlbyteswithparamsbackslashandbufferputparamsto-unknown--undefined-mysqlnobackslashescapes-boolean-usebuffer-uint8array-uint8arrayarraybufferlike)(putParamsTo: `unknown`\\[] | `undefined`, mysqlNoBackslashEscapes: `boolean`, useBuffer: Uint8Array): Uint8Array\\\u003cArrayBufferLike\u003e\n\nThis function is the same as `encode()`, but with 3 mandatory parameters.\nThis is for optimal support of [this MySQL driver](https://deno.land/x/office_spirit_mysql).\n\n### [Sql.sqlSettings](generated-doc/class.Sql/README.md#-sqlsettings-sqlsettings)\n\nThis public property of [Sql](generated-doc/class.Sql/README.md) object contains the chosen SQL dialect (SqlMode) and quoting policy, that allows to whitelist identifiers in SQL fragments.\n\n\u003e 📄 Sql.[sqlSettings](generated-doc/class.Sql/README.md#-sqlsettings-sqlsettings): [SqlSettings](generated-doc/class.SqlSettings/README.md)\n\nIf you create the [Sql](generated-doc/class.Sql/README.md) object using `mysql` template function, it's [sqlSettings.mode](generated-doc/class.SqlSettings/README.md#-readonly-mode-sqlmode) will be set to [SqlMode.MYSQL](generated-doc/enum.SqlMode/README.md#mysql--0), for `pgsql` it will be [SqlMode.PGSQL](generated-doc/enum.SqlMode/README.md#pgsql--2), etc.\n\nIf you assign a different [SqlSettings](generated-doc/class.SqlSettings/README.md) object before calling [Sql.toString()](generated-doc/class.Sql/README.md#-tostringputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-string) or [Sql.encode()](generated-doc/class.Sql/README.md#-encodeputparamsto-unknown-mysqlnobackslashescapes-booleanfalse-usebuffer-uint8array-usebufferfrompos-number0-defaultparentname-uint8array-uint8arrayarraybufferlike), that different SQL dialect and policy will be used.\n\nThe quotation policy has either a whitelist or a blacklist of allowed identifiers, that can remain unquoted.\nThere're 2 separate lists for functions (any identifier that is followed by a parenthesis is considered to be a function name), and for other identifiers.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-rjwg\u003e/' \u003e /tmp/example-rjwg.ts\n// deno run /tmp/example-rjwg.ts\n\nimport {mysql as sql, SqlSettings, SqlMode} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst value1 = \"The string is: 'name'. The backslash is: \\\\\";\nconst value2 = 123.4;\nconst value3 = null;\nconst expr1 = \"id=10 AND value IS NOT NULL\"; // we have 6 raw identifiers here: id, AND, value, IS, NOT, NULL\n\nlet select = sql`SELECT '${value1}', '${value2}', '${value3}' FROM t WHERE (${expr1})`;\n\nconsole.log(select+'');                        // SELECT 'The string is: ''name''. The backslash is: \\\\', 123.4, NULL FROM t WHERE (`id`=10 AND `value` IS NOT NULL)\nconsole.log(select.toString(undefined, true)); // SELECT 'The string is: ''name''. The backslash is: \\', 123.4, NULL FROM t WHERE (`id`=10 AND `value` IS NOT NULL)\n\nselect.sqlSettings = new SqlSettings(SqlMode.MYSQL, false, 'id not'); // in expr1 will quote: AND, value, IS, NULL\nconsole.log(select+'');                        // SELECT 'The string is: ''name''. The backslash is: \\\\', 123.4, NULL FROM t WHERE (id=10 `AND` `value` `IS` NOT `NULL`)\n\nselect.sqlSettings = new SqlSettings(SqlMode.PGSQL, false, '!id not'); // in expr1 will quote: id, NOT\nconsole.log(select+'');                        // SELECT 'The string is: ''name''. The backslash is: \\', 123.4, NULL FROM t WHERE (\"id\"=10 AND value IS \"NOT\" NULL)\n```\n\nTo create a new [SqlSettings](generated-doc/class.SqlSettings/README.md) object, that can be assigned to [Sql.sqlSettings](generated-doc/class.Sql/README.md#-sqlsettings-sqlsettings) property, provide the SQL dialect (SqlMode), the whitelist/blacklist of `idents`, and the same for `functions`.\nIf the list starts with `!`-char - it's the blacklist. Identifiers in the list are separated with spaces.\n\n\u003e 🔧 SqlSettings.[constructor](generated-doc/class.SqlSettings/README.md#-constructormode-sqlmode-usearrow-booleanfalse-idents-string-functions-string)(mode: [SqlMode](generated-doc/enum.SqlMode/README.md), useArrow: `boolean`=false, idents?: `string`, functions?: `string`)\n\nIf `idents` and/or `functions` argument is omitted or `undefined`, the default value is used.\n\nFor `idents` the default value is: `AGAINST AND AS ASC BETWEEN CASE CHAR DAY DESC DISTINCT ELSE END HOUR INTERVAL IS LIKE MATCH MICROSECOND MINUTE MONTH NOT NULL OR SECOND SEPARATOR THEN WEEK WHEN XOR YEAR`.\n\nFor `functions` is: `! FROM HAVING JOIN LIMIT OFFSET ON SELECT WHERE`.\n\nTo print the default policy, you can do:\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-c5vl\u003e/' \u003e /tmp/example-c5vl.ts\n// deno run /tmp/example-c5vl.ts\n\nimport {SqlSettings, SqlMode} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nlet settings = new SqlSettings(SqlMode.MYSQL);\n\nconsole.log('Identifiers: ', settings.idents);\nconsole.log('Functions: ', settings.functions);\n```\n\nPlease note, that quoting policy is only applied to safe SQL fragments that you embed using `` sql`(${expr})` `` or `` sql`${expr}` ``.\n\nIf you want to use custom policy all the time, you can write your own version of `sql()` function:\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-mbya\u003e/' \u003e /tmp/example-mbya.ts\n// deno run /tmp/example-mbya.ts\n\nimport {Sql, SqlSettings, SqlMode, SqlTable} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst DEFAULT_SETTINGS = new SqlSettings(SqlMode.MYSQL, false, '!bad forbidden', 'calc_stats');\n\nconst sql = new Proxy\n(\tfunction sql(strings: readonly string[], ...params: unknown[])\n\t{\treturn new Sql(DEFAULT_SETTINGS, undefined, strings, params);\n\t} as Record\u003cstring, SqlTable\u003e \u0026 {(strings: readonly string[], ...params: unknown[]): SqlTable},\n\t{\tget(_target, tableName)\n\t\t{\tif (typeof(tableName) != 'string')\n\t\t\t{\tthrow new Error(\"Table name must be string\");\n\t\t\t}\n\t\t\treturn new SqlTable(DEFAULT_SETTINGS, tableName);\n\t\t}\n\t}\n);\n\nconst expr = \"calc_stats(bad, good)\";\nconst s = sql`CALL ${expr}`;\nconsole.log('' + s); // prints: CALL calc_stats(`bad`, good)\nconsole.log('' + sql.messages.where('bad=0 AND good=1').select()); // prints SELECT * FROM `messages` WHERE (`bad`=0 AND good=1)\n```\n\nIt's a good idea to create [SqlSettings](generated-doc/class.SqlSettings/README.md) object once, and reuse it, because creating new instance takes time (it builds words index).\n\n## Generate SELECT, INSERT, UPDATE, DELETE and TRUNCATE queries from parts\n\nAs mentioned above there are the following string-template functions:\n\n- mysql and mysqlOnly\n- pgsql and pgsqlOnly\n- sqlite and sqliteOnly\n- mssql and mssqlOnly\n\nThese are actually fake functions implemented through `Proxy` class. They act as string-template functions, but they're also act like objects from which\nyou can ask properties. Each property that you ask from the object returns instance of [SqlTable](generated-doc/class.SqlTable/README.md) class that represents a table in your database, and you can\ngenerate SELECT, INSERT, UPDATE, DELETE and TRUNCATE queries for this table.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-y5hf\u003e/' \u003e /tmp/example-y5hf.ts\n// deno run /tmp/example-y5hf.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconsole.log('' + sql.messages.where(\"id=1\").select()); // prints: SELECT * FROM `messages` WHERE (`id`=1)\n```\n\n[SqlTable](generated-doc/class.SqlTable/README.md) class extends [Sql](generated-doc/class.Sql/README.md).\n\n```ts\nlet table: SqlTable = sql[tableName];\n```\n\n[SqlTable](generated-doc/class.SqlTable/README.md) class has the following methods:\n\n- [SqlTable.join()](generated-doc/class.SqlTable/README.md#-jointablename-string-alias-string-onexpr-string--sql-this): this\n- [SqlTable.leftJoin()](generated-doc/class.SqlTable/README.md#-leftjointablename-string-alias-string-onexpr-string--sql-this): this\n- [SqlTable.where()](generated-doc/class.SqlTable/README.md#-wherewhereexpr-string--sql-this): this\n- [SqlTable.whereRawSql()](generated-doc/class.SqlTable/README.md#-whererawsqlwhererawsql-sql-this): this\n- [SqlTable.groupBy()](generated-doc/class.SqlTable/README.md#-groupbygroupbyexprs-string--readonlyarraystring--sql-havingexpr-string--sql-this): this\n- [SqlTable.select()](generated-doc/class.SqlTable/README.md#-selectcolumns-string--readonlyarraystring--sql-orderby-orderby-offset-number0-limit-number0-this): this\n- [SqlTable.update()](generated-doc/class.SqlTable/README.md#-updaterow-recordstring-unknown-this): this\n- [SqlTable.delete()](generated-doc/class.SqlTable/README.md#-delete-this): this\n- [SqlTable.insert()](generated-doc/class.SqlTable/README.md#-insertrows-iterablerecordstring-unknown-onconflictdo---nothing--replace--update--patch-this): this\n- [SqlTable.insertFrom()](generated-doc/class.SqlTable/README.md#-insertfromnames-readonlyarraystring-select-sql-onconflictdo---nothing--replace-this): this\n- [SqlTable.truncate()](generated-doc/class.SqlTable/README.md#-truncate-this): this\n\nAll these methods only log what you ask, and the actual query generation will happen when you convert the object to string or bytes.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-jrt0\u003e/' \u003e /tmp/example-jrt0.ts\n// deno run /tmp/example-jrt0.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nlet s = sql.messages.join('content', 'c', 'content_id = c.id').where(\"id=1\").select(\"c.*\");\n// Now s is object that remembers what joins, columns and conditions you wanted to generate\n// So, let's convert the object to string to trigger the query generation\nconsole.log('' + s); // prints: SELECT `c`.* FROM `messages` AS `b` INNER JOIN `content` AS `c` ON (`b`.content_id = `c`.id) WHERE (`b`.id=1)\n```\n\n### [SqlTable.join()](generated-doc/class.SqlTable/README.md#-jointablename-string-alias-string-onexpr-string--sql-this)\n\n\u003e ⚙ SqlTable.[join](generated-doc/class.SqlTable/README.md#-jointablename-string-alias-string-onexpr-string--sql-this)(tableName: `string`, alias: `string`=\"\", onExpr: `string` | [Sql](generated-doc/class.Sql/README.md)=\"\"): `this`\n\nAdds an INNER (if `onExpr` is given) or a CROSS join (if `onExpr` is blank).\n\nThis method can be called multiple times.\n\n### [SqlTable.leftJoin()](generated-doc/class.SqlTable/README.md#-leftjointablename-string-alias-string-onexpr-string--sql-this)\n\n\u003e ⚙ SqlTable.[leftJoin](generated-doc/class.SqlTable/README.md#-leftjointablename-string-alias-string-onexpr-string--sql-this)(tableName: `string`, alias: `string`, onExpr: `string` | [Sql](generated-doc/class.Sql/README.md)): `this`\n\nLike [join()](generated-doc/class.SqlTable/README.md#-jointablename-string-alias-string-onexpr-string--sql-this), but adds a LEFT JOIN.\n\n### [SqlTable.where()](generated-doc/class.SqlTable/README.md#-wherewhereexpr-string--sql-this)\n\n\u003e ⚙ SqlTable.[where](generated-doc/class.SqlTable/README.md#-wherewhereexpr-string--sql-this)(whereExpr: `string` | [Sql](generated-doc/class.Sql/README.md)): `this`\n\nAdds WHERE condition for SELECT, UPDATE and DELETE queries.\n\nYou can call [SqlTable.select()](generated-doc/class.SqlTable/README.md#-selectcolumns-string--readonlyarraystring--sql-orderby-orderby-offset-number0-limit-number0-this), [SqlTable.update()](generated-doc/class.SqlTable/README.md#-updaterow-recordstring-unknown-this) and [SqlTable.delete()](generated-doc/class.SqlTable/README.md#-delete-this) only after calling [SqlTable.where()](generated-doc/class.SqlTable/README.md#-wherewhereexpr-string--sql-this), or an exception will be thrown.\nTo explicitly allow working on the whole table, call `sqlTable.where('')` (with empty condition).\n\n### [SqlTable.whereRawSql()](generated-doc/class.SqlTable/README.md#-whererawsqlwhererawsql-sql-this)\n\n\u003e ⚙ SqlTable.[whereRawSql](generated-doc/class.SqlTable/README.md#-whererawsqlwhererawsql-sql-this)(whereRawSql: [Sql](generated-doc/class.Sql/README.md)): `this`\n\nLike [SqlTable.where()](generated-doc/class.SqlTable/README.md#-wherewhereexpr-string--sql-this), but adds an unsafe raw SQL condition.\n\n### [SqlTable.groupBy()](generated-doc/class.SqlTable/README.md#-groupbygroupbyexprs-string--readonlyarraystring--sql-havingexpr-string--sql-this)\n\n\u003e ⚙ SqlTable.[groupBy](generated-doc/class.SqlTable/README.md#-groupbygroupbyexprs-string--readonlyarraystring--sql-havingexpr-string--sql-this)(groupByExprs: `string` | ReadonlyArray\\\u003c`string`\u003e | [Sql](generated-doc/class.Sql/README.md), havingExpr: `string` | [Sql](generated-doc/class.Sql/README.md)=\"\"): `this`\n\nAdds GROUP BY expressions, and optionally a HAVING expression to the SELECT query.\n\nIf `groupByExprs` is a string or an `Sql` object, it will represent a safe SQL fragment that contains comma-separated list of column expressions.\n\nIf it's `string[]`, it will be treated as array of column names.\n\n### [SqlTable.select()](generated-doc/class.SqlTable/README.md#-selectcolumns-string--readonlyarraystring--sql-orderby-orderby-offset-number0-limit-number0-this)\n\n\u003e ⚙ SqlTable.[select](generated-doc/class.SqlTable/README.md#-selectcolumns-string--readonlyarraystring--sql-orderby-orderby-offset-number0-limit-number0-this)(columns: `string` | ReadonlyArray\\\u003c`string`\u003e | [Sql](generated-doc/class.Sql/README.md)=\"\", orderBy: [OrderBy](generated-doc/type.OrderBy/README.md)=\"\", offset: `number`=0, limit: `number`=0): `this`\n\nGenerates a SELECT query.\n\nIf `columns` parameter is a string or an `Sql` object, it will represent columns as a safe SQL fragment.\n\nIf it's `string[]`, it will be treated as array of column names.\n\nEmpty string or array will represent `*`-wildcard (select all columns).\n\nOFFSET and LIMIT without ORDER BY are not supported on Microsoft SQL Server.\n\n### [SqlTable.update()](generated-doc/class.SqlTable/README.md#-updaterow-recordstring-unknown-this)\n\n\u003e ⚙ SqlTable.[update](generated-doc/class.SqlTable/README.md#-updaterow-recordstring-unknown-this)(row: Record\\\u003c`string`, `unknown`\u003e): `this`\n\nGenerates an UPDATE query. You can update with joins, but if the first join is a LEFT JOIN, such query is not supported by PostgreSQL.\nColumns of the base table (not joined) will be updated.\n\n### [SqlTable.delete()](generated-doc/class.SqlTable/README.md#-delete-this)\n\n\u003e ⚙ SqlTable.[delete](generated-doc/class.SqlTable/README.md#-delete-this)(): `this`\n\nGenerates a DELETE query. You can delete with joins, but if the first join is a LEFT JOIN, such query is not supported by PostgreSQL.\nWill delete from the base table (not joined).\n\n### [SqlTable.insert()](generated-doc/class.SqlTable/README.md#-insertrows-iterablerecordstring-unknown-onconflictdo---nothing--replace--update--patch-this)\n\n\u003e ⚙ SqlTable.[insert](generated-doc/class.SqlTable/README.md#-insertrows-iterablerecordstring-unknown-onconflictdo---nothing--replace--update--patch-this)(rows: Iterable\\\u003cRecord\\\u003c`string`, `unknown`\u003e\u003e, onConflictDo: \u003cmark\u003e\"\"\u003c/mark\u003e | \u003cmark\u003e\"nothing\"\u003c/mark\u003e | \u003cmark\u003e\"replace\"\u003c/mark\u003e | \u003cmark\u003e\"update\"\u003c/mark\u003e | \u003cmark\u003e\"patch\"\u003c/mark\u003e=\"\"): `this`\n\nGenerates an INSERT query.\n\n- `onConflictDo=='nothing'` is only supported for MySQL, PostgreSQL and SQLite. Ignores (doesn't insert) conflicting rows (if unique constraint fails).\n- `onConflictDo=='replace'` is only supported for MySQL and SQLite. If duplicate key, deletes the whole conflicting row, and inserts new in place of it.\n- `onConflictDo=='update'` is only supported for MySQL and SQLite. If duplicate key, updates the existing record with the new values.\n- `onConflictDo=='patch'` is only supported for MySQL. If duplicate key, updates only **empty** (null, 0 or '') columns of the existing record with the new values.\n\n### [SqlTable.insertFrom()](generated-doc/class.SqlTable/README.md#-insertfromnames-readonlyarraystring-select-sql-onconflictdo---nothing--replace-this)\n\n\u003e ⚙ SqlTable.[insertFrom](generated-doc/class.SqlTable/README.md#-insertfromnames-readonlyarraystring-select-sql-onconflictdo---nothing--replace-this)(names: ReadonlyArray\\\u003c`string`\u003e, select: [Sql](generated-doc/class.Sql/README.md), onConflictDo: \u003cmark\u003e\"\"\u003c/mark\u003e | \u003cmark\u003e\"nothing\"\u003c/mark\u003e | \u003cmark\u003e\"replace\"\u003c/mark\u003e=\"\"): `this`\n\nGenerates \"INSERT INTO (...) SELECT ...\" query.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-hwat\u003e/' \u003e /tmp/example-hwat.ts\n// deno run /tmp/example-hwat.ts\n\nimport {mysql as sql} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nlet s = sql.t_log.insertFrom(['c1', 'c2'], sql.t_log_bak.where('id\u003c=100').select(['c1', 'c2']));\nconsole.log('' + s); // prints: INSERT INTO `t_log` (`c1`, `c2`) SELECT `c1`, `c2` FROM `t_log_bak` WHERE (`id`\u003c=100)\n```\n\n### [SqlTable.truncate()](generated-doc/class.SqlTable/README.md#-truncate-this)\n\n\u003e ⚙ SqlTable.[truncate](generated-doc/class.SqlTable/README.md#-truncate-this)(): `this`\n\nGenerates TRUNCATE TABLE query where supported (all but SQLite), and for others generates \"DELETE FROM\".\n\n### Extending SqlTable\n\n[SqlTable](generated-doc/class.SqlTable/README.md) class extends [Sql](generated-doc/class.Sql/README.md). You can also extend [SqlTable](generated-doc/class.SqlTable/README.md) itself in order to add your own custom methods.\nOne practical reason for this is to override it's protected method called [appendTableName()](generated-doc/class.SqlTable/README.md#-protected-appendtablenametablename-string-string).\nThis method controls how table names are generated in the queries.\nIn this method you can for example add prefixes to each table name, or to qualify some or all tables with schema prefix.\n\nThe default implementation in [SqlTable](generated-doc/class.SqlTable/README.md) class looks like this:\n\n```ts\nprotected appendTableName(tableName: string)\n{\tthis.append(sql`\"${tableName}\"`);\n\treturn tableName;\n}\n```\n\nThis method is called every time a quoted table name must be appended to the query.\n\nAfter this function appended the (converted) table name, it must then return this name without qualifiers.\n\nThe default implementation shown above doesn't change the name, and doesn't add qualifiers.\n\nThe following implementation will convert table names.\nIn this implementation i redefine the `sql` proxy object in the same way that this library does.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-w4qu\u003e/' \u003e /tmp/example-w4qu.ts\n// deno run /tmp/example-w4qu.ts\n\nimport {SqlSettings, SqlMode, SqlTable} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst SQL_SETTINGS_MYSQL = new SqlSettings(SqlMode.MYSQL);\n\nclass SqlTableCustom extends SqlTable\n{\tprivate schema = 'sc';\n\n\tprotected override appendTableName(tableName: string)\n\t{\ttableName = 't_' + tableName;\n\t\tthis.append(!this.schema ? sql`\"${tableName}\"` : sql`\"${this.schema}\".\"${tableName}\"`);\n\t\treturn tableName;\n\t}\n}\n\nconst sql = new Proxy\n(\tfunction sql(strings: readonly string[], ...params: unknown[])\n\t{\treturn new SqlTableCustom(SQL_SETTINGS_MYSQL, '', strings, params);\n\t} as Record\u003cstring, SqlTableCustom\u003e \u0026 {(strings: readonly string[], ...params: unknown[]): SqlTableCustom},\n\t{\tget(_target, table_name)\n\t\t{\tif (typeof(table_name) != 'string')\n\t\t\t{\tthrow new Error(\"Table name must be string\");\n\t\t\t}\n\t\t\treturn new SqlTableCustom(SQL_SETTINGS_MYSQL, table_name);\n\t\t}\n\t}\n);\n\nconsole.log('' + sql.messages.where('id=1').select()); // prints: SELECT * FROM `sc`.`t_messages` WHERE (`id`=1)\n```\n\nAnother reason for adding custom methods to [SqlTable](generated-doc/class.SqlTable/README.md) is to add a `query()` method that will cooperate with your database driver.\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-lgsn\u003e/' \u003e /tmp/example-lgsn.ts\n// deno run /tmp/example-lgsn.ts\n\nimport {SqlSettings, SqlMode, SqlTable} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\nconst DEFAULT_SETTINGS = new SqlSettings(SqlMode.MYSQL);\n\n// Class for communication with database server (through your preferred driver)\nclass Connection\n{\tconstructor(private dsn: string)\n\t{\t// TODO: Lazy connect to server\n\t\tconsole.log(`Connecting to ${this.dsn}`);\n\t}\n\n\tasync query(sql: string, params: unknown[])\n\t{\t// TODO: Make actual query to the server\n\t\tconsole.log(`Querying on ${this.dsn}: ${sql} -- `, params);\n\t}\n\n\tasync close()\n\t{\t// TODO: disconnect\n\t\tconsole.log(`Disconnecting from ${this.dsn}`);\n\t}\n}\n\n// Extend `SqlTable` to add custom methods\nclass SqlTableCustom extends SqlTable\n{\tconstructor(private connection: Connection, sqlSettings: SqlSettings, tableName: string, strings?: readonly string[], params?: unknown[])\n\t{\tsuper(sqlSettings, tableName, strings, params);\n\t}\n\n\t// My custom method on `SqlTable`\n\tquery()\n\t{\tconst putParamsTo: unknown[] = [];\n\t\treturn this.connection.query(this.toString(putParamsTo), putParamsTo);\n\t}\n}\n\n// Connect to the database, and return object for querying it.\n// `dsn` - \"Data Source Name\" (usually server host, port and database name)\nfunction connect(dsn: string)\n{\tconst connection = new Connection(dsn);\n\treturn new Proxy\n\t(\tfunction(strings: readonly string[], ...params: unknown[])\n\t\t{\treturn new SqlTableCustom(connection, DEFAULT_SETTINGS, '', strings, params);\n\t\t} as\n\t\tRecord\u003cstring, SqlTableCustom\u003e \u0026\n\t\t{\t(strings: readonly string[], ...params: unknown[]): SqlTableCustom;\n\t\t\t[Symbol.asyncDispose](): Promise\u003cvoid\u003e;\n\t\t},\n\t\t{\tget(_target, tableName)\n\t\t\t{\tif (typeof(tableName) != 'string')\n\t\t\t\t{\tif (tableName == Symbol.asyncDispose)\n\t\t\t\t\t{\treturn () =\u003e connection.close();\n\t\t\t\t\t}\n\t\t\t\t\tthrow new Error(\"Table name must be string\");\n\t\t\t\t}\n\t\t\t\treturn new SqlTableCustom(connection, DEFAULT_SETTINGS, tableName);\n\t\t\t}\n\t\t}\n\t);\n}\n\n// Get connection\nawait using sql = connect('localhost:3306');\n\n// Query\nawait sql`SELECT * FROM links WHERE url='${import.meta.url}'`.query(); // prints: Querying on localhost:3306: SELECT * FROM links WHERE url='...'\nawait sql.messages.where('id=1').select().query(); // prints: Querying on localhost:3306: SELECT * FROM `messages` WHERE (`id`=1)\n```\n\n### Arrow (`-\u003e`) operator\n\nMySQL 8 uses arrow operator to access JSON objects. However this library offers you to utilize this operator (not only on MySQL) for accessing columns from foreign tables.\nTo opt-in to this feature, create [SqlSettings](generated-doc/class.SqlSettings/README.md) object with second argument `true`.\n\n```ts\nconst settings = new SqlSettings(SqlMode.MYSQL_ONLY, true);\n```\n\nThen override [SqlTable.onJoinForeign()](generated-doc/class.SqlTable/README.md#-protected-onjoinforeign_tablename-string-_alias-string-_columnname-string-string) method that will call [SqlTable.leftJoin()](generated-doc/class.SqlTable/README.md#-leftjointablename-string-alias-string-onexpr-string--sql-this) (or [SqlTable.join()](generated-doc/class.SqlTable/README.md#-jointablename-string-alias-string-onexpr-string--sql-this)).\n\n```sql\nCREATE TABLE products\n(\tid integer PRIMARY KEY AUTO_INCREMENT,\n\tname varchar(100),\n\tprice float\n);\n\nCREATE TABLE transactions\n(\tid integer PRIMARY KEY AUTO_INCREMENT,\n\tproduct_id integer,\n\ttime datetime,\n\tFOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE ON UPDATE CASCADE\n);\n```\n\n```ts\n// To download and run this example:\n// curl 'https://raw.githubusercontent.com/jeremiah-shaulov/polysql/v2.0.18/README.md' | perl -ne 's/^\u003e //; $y=$1 if /^```(.)?/; print $_ if $y\u0026\u0026$m; $m=$y\u0026\u0026$m+/\u003cexample-wk20\u003e/' \u003e /tmp/example-wk20.ts\n// deno run /tmp/example-wk20.ts\n\nimport {SqlSettings, SqlMode, SqlTable} from 'https://deno.land/x/polysql@v2.0.18/mod.ts';\n\ntype TableInfo =\n{\tforeignKeys?: Record\u003cstring, {refTable: string, onExpr: (baseAlias: string, refAlias: string) =\u003e string}\u003e;\n};\n\nconst SCHEMA: Record\u003cstring, TableInfo|undefined\u003e =\n{\tusers: undefined,\n\tproducts: undefined,\n\ttransactions:\n\t{\tforeignKeys:\n\t\t{\tproduct_id: {refTable: 'products', onExpr: (b, r) =\u003e `${b}.product_id = ${r}.id`}\n\t\t}\n\t}\n};\n\nclass SqlTableCustom extends SqlTable\n{\tprotected override appendTableName(tableName: string)\n\t{\tif (!(tableName in SCHEMA))\n\t\t{\tthrow new Error(`Unknown table: ${tableName}`);\n\t\t}\n\t\treturn super.appendTableName(tableName);\n\t}\n\n\tprotected override onJoinForeign(tableName: string, alias: string, columnName: string)\n\t{\tconst ref = SCHEMA[tableName.toLocaleLowerCase()]?.foreignKeys?.[columnName.toLocaleLowerCase()];\n\t\tif (ref)\n\t\t{\tconst {refTable, onExpr} = ref;\n\t\t\tconst refAlias = this.genAlias(refTable);\n\t\t\tthis.leftJoin(refTable, refAlias, onExpr(alias, refAlias));\n\t\t\treturn refAlias;\n\t\t}\n\t}\n}\n\nconst SQL_SETTINGS_MYSQL = new SqlSettings(SqlMode.MYSQL_ONLY, true);\n\nconst sql = new Proxy\n(\tfunction sql(strings: readonly string[], ...params: unknown[])\n\t{\treturn new SqlTableCustom(SQL_SETTINGS_MYSQL, '', strings, params);\n\t} as Record\u003cstring, SqlTableCustom\u003e \u0026 {(strings: readonly string[], ...params: unknown[]): SqlTableCustom},\n\t{\tget(_target, table_name)\n\t\t{\tif (typeof(table_name) != 'string')\n\t\t\t{\tthrow new Error(\"Table name must be string\");\n\t\t\t}\n\t\t\treturn new SqlTableCustom(SQL_SETTINGS_MYSQL, table_name);\n\t\t}\n\t}\n);\n\nconsole.log('' + sql.transactions.where(`id = 1`).select('product_id-\u003ename, product_id-\u003eprice'));\n// prints: SELECT `p`.`name`, `p`.`price` FROM `transactions` AS `t` LEFT JOIN `products` AS `p` ON (`t`.product_id = `p`.id) WHERE (`t`.id = 1)\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremiah-shaulov%2Fpolysql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeremiah-shaulov%2Fpolysql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremiah-shaulov%2Fpolysql/lists"}