imodel-pg 0.19.1 → 0.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.d.mts +1 -1
  2. package/index.mjs +187 -16
  3. package/package.json +1 -1
package/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * imodel v0.19.1
2
+ * imodel v0.19.3
3
3
  * (c) 2019-2026 undefined
4
4
  * @license undefined
5
5
  */
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * imodel v0.19.1
2
+ * imodel v0.19.3
3
3
  * (c) 2019-2026 undefined
4
4
  * @license undefined
5
5
  */
@@ -80,6 +80,7 @@ function getBaseType(s, {
80
80
  case 'json':
81
81
  return 'json';
82
82
  }
83
+ return s;
83
84
  }
84
85
  /**
85
86
  *
@@ -522,14 +523,17 @@ function buildSelect(env, selectList) {
522
523
  offset,
523
524
  limit,
524
525
  group,
525
- having
526
+ having,
527
+ lock
526
528
  } of selectList) {
527
529
  const Table = Sql.Table(table);
528
530
  const selectSql = getSelect(Table, select);
529
531
  const orderSql = getOrder(sort);
530
532
  const limitSql = limit && limit > 0 ? Sql('LIMIT', limit) : undefined;
531
533
  const offsetSql = offset && offset > 0 ? Sql('OFFSET', offset) : undefined;
532
- list.push(Sql(Sql`SELECT`, distinct ? Sql`DISTINCT` : undefined, Sql`${selectSql}`, Sql`FROM ${Table}`, getWhere(env, where), orderSql, limitSql, offsetSql, group?.length ? Sql`GROUP BY ${Sql`,`.glue(group.map(v => Sql.Field(v)))}` : undefined, getWhere(env, having, 'HAVING')));
534
+ list.push(Sql(Sql`SELECT`, distinct ? Sql`DISTINCT` : undefined, Sql`${selectSql}`, Sql`FROM ${Table}`, getWhere(env, where), orderSql, limitSql, offsetSql, group?.length ? Sql`GROUP BY ${Sql`,`.glue(group.map(v => Sql.Field(v)))}` : undefined, getWhere(env, having, 'HAVING'),
535
+ // eslint-disable-next-line no-nested-ternary
536
+ lock === 'X' ? Sql`FOR UPDATE` : lock === 'S' ? Sql`FOR SHARE` : undefined));
533
537
  }
534
538
  if (!list.length) {
535
539
  return null;
@@ -1316,6 +1320,35 @@ async function loadTables(env, query, tables, schema) {
1316
1320
  return dbTables;
1317
1321
  }
1318
1322
 
1323
+ /**
1324
+ * 生成 PostgreSQL ALTER TABLE 语句中强制类型转换的 USING 子句
1325
+ * @param {Sql} COLUMN - 列名
1326
+ * @param {boolean?} [array]
1327
+ * @param {boolean?} [nullable]
1328
+ * @param {boolean?} [oldArray]
1329
+ * @param {Sql?} [def]
1330
+ * @returns {Sql}
1331
+ */
1332
+ function generateUsingClause(COLUMN, array, nullable, oldArray, def) {
1333
+ if (array && !oldArray) {
1334
+ if (nullable) {
1335
+ return Sql`(CASE
1336
+ WHEN ${COLUMN} IS NULL THEN NULL
1337
+ ELSE ARRAY[${COLUMN}]
1338
+ END)`;
1339
+ }
1340
+ return Sql`(CASE
1341
+ WHEN ${COLUMN} IS NULL THEN ARRAY[]
1342
+ ELSE ARRAY[${COLUMN}]
1343
+ END)`;
1344
+ }
1345
+ const val = oldArray && !array ? Sql`${COLUMN}[1]` : Sql`${COLUMN}`;
1346
+ if (nullable || !def) {
1347
+ return val;
1348
+ }
1349
+ return Sql`COALESCE(${val}, ${def})`;
1350
+ }
1351
+
1319
1352
  /** @import { Environment, IConnection } from 'imodel' */
1320
1353
  /** @import { PgEnvTrans } from '../index.mjs' */
1321
1354
  /** @import { DBIndex, DBTable, DBColumn } from 'imodel' */
@@ -1639,15 +1672,18 @@ WHERE table_name = ${table} AND constraint_type = 'PRIMARY KEY'
1639
1672
  size
1640
1673
  }, array);
1641
1674
  const oldType = getType(old.type, old, old.array);
1642
- if (newType !== oldType) {
1643
- COLUMNs.push(Sql`${COLUMN} TYPE ${Sql(newType)}`);
1675
+ const def = getDefault(env, defaultValue, type, array);
1676
+ if (newType !== oldType || !nullable && old.nullable) {
1677
+ console.log(newType, oldType);
1678
+ const usingClause = generateUsingClause(Sql.Field(fieldName), array, nullable, old.array, def);
1679
+ const type = Sql(newType);
1680
+ COLUMNs.push(Sql`${COLUMN} TYPE ${type} USING ${usingClause}::${type}`);
1644
1681
  // TODO: USING "description"::int2
1645
1682
  }
1646
1683
  if (Boolean(nullable) !== Boolean(old.nullable)) {
1647
1684
  COLUMNs.push(nullable ? Sql`${COLUMN} DROP NOT NULL` : Sql`${COLUMN} SET NOT NULL`);
1648
1685
  }
1649
1686
  if (!defaultIsQe(defaultValue, old.default)) {
1650
- const def = getDefault(env, defaultValue, type, array);
1651
1687
  COLUMNs.push(def ? Sql`${COLUMN} SET DEFAULT ${def}` : Sql`${COLUMN} DROP DEFAULT`);
1652
1688
  }
1653
1689
  }
@@ -1698,6 +1734,144 @@ async function syncTables(env, query, tables, schema) {
1698
1734
  }
1699
1735
  }
1700
1736
 
1737
+ /** @import { Environment } from 'imodel' */
1738
+ /** @import { PgEnvTrans } from './index.mjs' */
1739
+ /**
1740
+ *
1741
+ * @param {string?} [detail]
1742
+ * @returns
1743
+ */
1744
+ function parseUniqueViolationErrorColumns(detail) {
1745
+ const match = /^[^"]*"(.*)"[^"]*$/.exec(detail || '');
1746
+ const colsStr = match?.[1];
1747
+ if (!colsStr) {
1748
+ return [];
1749
+ }
1750
+ const cols = [];
1751
+ let i = 1;
1752
+ for (; i < colsStr.length; i++) {
1753
+ const char = colsStr[i];
1754
+ if (char === ' ' || char === ',') {
1755
+ continue;
1756
+ }
1757
+ if (char === ')') {
1758
+ break;
1759
+ }
1760
+ const list = [];
1761
+ if (char === '"') {
1762
+ i++;
1763
+ for (; i < colsStr.length; i++) {
1764
+ const char = colsStr[i];
1765
+ if (char === '"') {
1766
+ if (colsStr[i + 1] !== '"') {
1767
+ break;
1768
+ }
1769
+ i++;
1770
+ }
1771
+ list.push(char);
1772
+ }
1773
+ } else {
1774
+ for (; i < colsStr.length; i++) {
1775
+ const char = colsStr[i];
1776
+ if (char === ' ') {
1777
+ continue;
1778
+ }
1779
+ if (char === ')' || char === ',') {
1780
+ i--;
1781
+ break;
1782
+ }
1783
+ list.push(char);
1784
+ }
1785
+ }
1786
+ cols.push(list.join(''));
1787
+ continue;
1788
+ }
1789
+ return cols;
1790
+ }
1791
+ /** @type {Record<string, (e: pg.DatabaseError, env: Environment<PgEnvTrans>) => any>} */
1792
+ const errors = {
1793
+ '22P02'(error, {
1794
+ errors
1795
+ }) {
1796
+ return new errors.InvalidInputSyntaxError(error.message);
1797
+ },
1798
+ 23502(error, {
1799
+ errors
1800
+ }) {
1801
+ const {
1802
+ table,
1803
+ column
1804
+ } = error;
1805
+ if (!table || !column) {
1806
+ return;
1807
+ }
1808
+ return new errors.NotNullViolationError(table, column);
1809
+ },
1810
+ 23505(error, {
1811
+ errors
1812
+ }) {
1813
+ const {
1814
+ table,
1815
+ detail
1816
+ } = error;
1817
+ if (!table) {
1818
+ return;
1819
+ }
1820
+ return new errors.UniqueViolationError(table, parseUniqueViolationErrorColumns(detail));
1821
+ },
1822
+ 23503(error, {
1823
+ errors
1824
+ }) {
1825
+ const {
1826
+ table,
1827
+ column,
1828
+ constraint
1829
+ } = error;
1830
+ if (!table || !column) {
1831
+ return;
1832
+ }
1833
+ return new errors.ForeignKeyViolationError(table, column, constraint);
1834
+ },
1835
+ 42883(error, {
1836
+ errors
1837
+ }) {
1838
+ return new errors.OperatorMismatchError(error.message);
1839
+ }
1840
+ };
1841
+ const errorGroup = {
1842
+ 22: 'data',
1843
+ 23: 'constraint',
1844
+ 24: 'cursor',
1845
+ 25: 'transaction'
1846
+ };
1847
+ /**
1848
+ *
1849
+ * @param {pg.DatabaseError} error
1850
+ * @param {Environment<PgEnvTrans>} env
1851
+ */
1852
+ function errorConvert(error, env) {
1853
+ if (!(error instanceof pg.DatabaseError)) {
1854
+ return error;
1855
+ }
1856
+ const {
1857
+ code
1858
+ } = error;
1859
+ if (!code) {
1860
+ throw error;
1861
+ }
1862
+ const convert = Object.hasOwn(errors, code) ? errors[code] : null;
1863
+ const converted = convert?.(error, env);
1864
+ if (converted) {
1865
+ return converted;
1866
+ }
1867
+ if (typeof code !== 'string' || code.length !== 5) {
1868
+ return error;
1869
+ }
1870
+ const group = code.slice(0, 2);
1871
+ const groupCode = Object.hasOwn(errorGroup, group) && errorGroup[group];
1872
+ return new env.errors.ConnectionError(groupCode ? `${groupCode}::pg:${code}` : `:pg:${code}`, error.message);
1873
+ }
1874
+
1701
1875
  /** @import { Environment, IConnection } from 'imodel' */
1702
1876
  const {
1703
1877
  types
@@ -1826,22 +2000,19 @@ function index (pool, version) {
1826
2000
  const {
1827
2001
  transaction
1828
2002
  } = env;
1829
- const transactionClient = transaction?.client;
1830
- if (transactionClient) {
1831
- const client = await Promise.resolve(transactionClient);
1832
- return client.query({
1833
- text,
1834
- values: [...values]
1835
- });
1836
- }
1837
- const client = await connect();
2003
+ const transactionClient = await Promise.resolve(transaction?.client);
2004
+ const client = transactionClient || (await connect());
1838
2005
  try {
1839
2006
  return await client.query({
1840
2007
  text,
1841
2008
  values: [...values]
1842
2009
  });
2010
+ } catch (error) {
2011
+ throw errorConvert(error, env) || error;
1843
2012
  } finally {
1844
- client.release();
2013
+ if (!transactionClient) {
2014
+ client.release();
2015
+ }
1845
2016
  }
1846
2017
  }
1847
2018
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imodel-pg",
3
- "version": "0.19.1",
3
+ "version": "0.19.3",
4
4
  "dependencies": {
5
5
  "pg": "^8.13.3",
6
6
  "tagged-sql": "^0.9.0"