viyv-db-postgres 0.1.0

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 (98) hide show
  1. package/LICENSE +21 -0
  2. package/dist/config.d.ts +37 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +15 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/db.d.ts +9 -0
  7. package/dist/db.d.ts.map +1 -0
  8. package/dist/db.js +13 -0
  9. package/dist/db.js.map +1 -0
  10. package/dist/error-mapper.d.ts +2 -0
  11. package/dist/error-mapper.d.ts.map +1 -0
  12. package/dist/error-mapper.js +24 -0
  13. package/dist/error-mapper.js.map +1 -0
  14. package/dist/index.d.ts +8 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +19 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/logical/cte-builder.d.ts +57 -0
  19. package/dist/logical/cte-builder.d.ts.map +1 -0
  20. package/dist/logical/cte-builder.js +167 -0
  21. package/dist/logical/cte-builder.js.map +1 -0
  22. package/dist/logical/data-ops.d.ts +19 -0
  23. package/dist/logical/data-ops.d.ts.map +1 -0
  24. package/dist/logical/data-ops.js +124 -0
  25. package/dist/logical/data-ops.js.map +1 -0
  26. package/dist/logical/embedding-ops.d.ts +24 -0
  27. package/dist/logical/embedding-ops.d.ts.map +1 -0
  28. package/dist/logical/embedding-ops.js +94 -0
  29. package/dist/logical/embedding-ops.js.map +1 -0
  30. package/dist/logical/migration-ops.d.ts +10 -0
  31. package/dist/logical/migration-ops.d.ts.map +1 -0
  32. package/dist/logical/migration-ops.js +201 -0
  33. package/dist/logical/migration-ops.js.map +1 -0
  34. package/dist/logical/query-ops.d.ts +18 -0
  35. package/dist/logical/query-ops.d.ts.map +1 -0
  36. package/dist/logical/query-ops.js +193 -0
  37. package/dist/logical/query-ops.js.map +1 -0
  38. package/dist/logical/registry.d.ts +27 -0
  39. package/dist/logical/registry.d.ts.map +1 -0
  40. package/dist/logical/registry.js +79 -0
  41. package/dist/logical/registry.js.map +1 -0
  42. package/dist/logical/schema-ops.d.ts +7 -0
  43. package/dist/logical/schema-ops.d.ts.map +1 -0
  44. package/dist/logical/schema-ops.js +311 -0
  45. package/dist/logical/schema-ops.js.map +1 -0
  46. package/dist/logical/sql-parser.d.ts +11 -0
  47. package/dist/logical/sql-parser.d.ts.map +1 -0
  48. package/dist/logical/sql-parser.js +37 -0
  49. package/dist/logical/sql-parser.js.map +1 -0
  50. package/dist/logical/sql-table-refs.d.ts +18 -0
  51. package/dist/logical/sql-table-refs.d.ts.map +1 -0
  52. package/dist/logical/sql-table-refs.js +203 -0
  53. package/dist/logical/sql-table-refs.js.map +1 -0
  54. package/dist/logical/sql-tokenizer.d.ts +20 -0
  55. package/dist/logical/sql-tokenizer.d.ts.map +1 -0
  56. package/dist/logical/sql-tokenizer.js +249 -0
  57. package/dist/logical/sql-tokenizer.js.map +1 -0
  58. package/dist/logical/type-utils.d.ts +11 -0
  59. package/dist/logical/type-utils.d.ts.map +1 -0
  60. package/dist/logical/type-utils.js +42 -0
  61. package/dist/logical/type-utils.js.map +1 -0
  62. package/dist/logical/where-builder.d.ts +6 -0
  63. package/dist/logical/where-builder.d.ts.map +1 -0
  64. package/dist/logical/where-builder.js +20 -0
  65. package/dist/logical/where-builder.js.map +1 -0
  66. package/dist/schema.d.ts +926 -0
  67. package/dist/schema.d.ts.map +1 -0
  68. package/dist/schema.js +78 -0
  69. package/dist/schema.js.map +1 -0
  70. package/dist/services/postgres-service.d.ts +40 -0
  71. package/dist/services/postgres-service.d.ts.map +1 -0
  72. package/dist/services/postgres-service.js +479 -0
  73. package/dist/services/postgres-service.js.map +1 -0
  74. package/dist/services/semantic-search-service.d.ts +16 -0
  75. package/dist/services/semantic-search-service.d.ts.map +1 -0
  76. package/dist/services/semantic-search-service.js +94 -0
  77. package/dist/services/semantic-search-service.js.map +1 -0
  78. package/dist/system-migrations/001_composite_pk.d.ts +4 -0
  79. package/dist/system-migrations/001_composite_pk.d.ts.map +1 -0
  80. package/dist/system-migrations/001_composite_pk.js +23 -0
  81. package/dist/system-migrations/001_composite_pk.js.map +1 -0
  82. package/dist/system-migrations/002_schema_log_index.d.ts +4 -0
  83. package/dist/system-migrations/002_schema_log_index.d.ts.map +1 -0
  84. package/dist/system-migrations/002_schema_log_index.js +13 -0
  85. package/dist/system-migrations/002_schema_log_index.js.map +1 -0
  86. package/dist/system-migrations/index.d.ts +9 -0
  87. package/dist/system-migrations/index.d.ts.map +1 -0
  88. package/dist/system-migrations/index.js +9 -0
  89. package/dist/system-migrations/index.js.map +1 -0
  90. package/dist/system-migrations/types.d.ts +18 -0
  91. package/dist/system-migrations/types.d.ts.map +1 -0
  92. package/dist/system-migrations/types.js +28 -0
  93. package/dist/system-migrations/types.js.map +1 -0
  94. package/dist/system-migrations.d.ts +25 -0
  95. package/dist/system-migrations.d.ts.map +1 -0
  96. package/dist/system-migrations.js +66 -0
  97. package/dist/system-migrations.js.map +1 -0
  98. package/package.json +44 -0
@@ -0,0 +1,203 @@
1
+ /**
2
+ * SQL 内の全テーブル参照を抽出する。
3
+ * クォート認識: 文字列リテラル・コメント内の FROM/JOIN は無視。
4
+ */
5
+ import { tokenizeSql } from './sql-tokenizer.js';
6
+ // FROM/JOIN 句を終了するキーワード(エイリアスと区別するため)
7
+ const CLAUSE_KEYWORDS = new Set([
8
+ 'SELECT',
9
+ 'WHERE',
10
+ 'JOIN',
11
+ 'LEFT',
12
+ 'RIGHT',
13
+ 'INNER',
14
+ 'OUTER',
15
+ 'CROSS',
16
+ 'FULL',
17
+ 'NATURAL',
18
+ 'ON',
19
+ 'ORDER',
20
+ 'GROUP',
21
+ 'HAVING',
22
+ 'LIMIT',
23
+ 'OFFSET',
24
+ 'UNION',
25
+ 'INTERSECT',
26
+ 'EXCEPT',
27
+ 'RETURNING',
28
+ 'FOR',
29
+ 'WINDOW',
30
+ 'FETCH',
31
+ 'LATERAL',
32
+ ]);
33
+ /** 意味トークンのみ(whitespace/comment を除く)のインデックスを返す */
34
+ function semanticTokens(tokens) {
35
+ const indices = [];
36
+ for (let i = 0; i < tokens.length; i++) {
37
+ if (tokens[i].type !== 'whitespace' && tokens[i].type !== 'comment') {
38
+ indices.push(i);
39
+ }
40
+ }
41
+ return indices;
42
+ }
43
+ /**
44
+ * テーブル名の後のオプショナルなエイリアスを読み飛ばす。
45
+ * パターン: [AS] <alias> — AS はオプション、alias は clause keyword でない word/quoted_ident
46
+ */
47
+ function skipAlias(tokens, semIdx, pos) {
48
+ if (pos >= semIdx.length)
49
+ return pos;
50
+ const t = tokens[semIdx[pos]];
51
+ // AS keyword → skip AS + alias
52
+ if (t.type === 'word' && t.value.toUpperCase() === 'AS') {
53
+ const afterAs = pos + 1;
54
+ return afterAs < semIdx.length ? afterAs + 1 : afterAs;
55
+ }
56
+ // Implicit alias: word that's not a clause keyword
57
+ if ((t.type === 'word' && !CLAUSE_KEYWORDS.has(t.value.toUpperCase())) ||
58
+ t.type === 'quoted_ident') {
59
+ return pos + 1;
60
+ }
61
+ return pos;
62
+ }
63
+ /** トークンから識別子名を取得(quoted_ident ならクォートを外す) */
64
+ function identName(token) {
65
+ if (token.type === 'quoted_ident') {
66
+ // Remove surrounding quotes and unescape ""
67
+ return token.value.slice(1, -1).replace(/""/g, '"');
68
+ }
69
+ return token.value;
70
+ }
71
+ /**
72
+ * SQL 内の全テーブル参照を抽出する。
73
+ *
74
+ * 検出パターン:
75
+ * FROM <table> [, <table>, ...]
76
+ * [LEFT|RIGHT|INNER|OUTER|CROSS|FULL|NATURAL] JOIN <table>
77
+ * WITH <name> AS ( → context='with'
78
+ */
79
+ export function extractTableRefs(sql) {
80
+ const tokens = tokenizeSql(sql);
81
+ const semIdx = semanticTokens(tokens);
82
+ const refs = [];
83
+ for (let si = 0; si < semIdx.length; si++) {
84
+ const token = tokens[semIdx[si]];
85
+ const upper = token.type === 'word' ? token.value.toUpperCase() : '';
86
+ // WITH <name> AS
87
+ if (upper === 'WITH') {
88
+ // Skip RECURSIVE if present
89
+ let nextSi = si + 1;
90
+ if (nextSi < semIdx.length &&
91
+ tokens[semIdx[nextSi]].type === 'word' &&
92
+ tokens[semIdx[nextSi]].value.toUpperCase() === 'RECURSIVE') {
93
+ nextSi++;
94
+ }
95
+ // Parse CTE definitions: name AS (...), name AS (...), ...
96
+ while (nextSi < semIdx.length) {
97
+ const nameToken = tokens[semIdx[nextSi]];
98
+ if (nameToken.type !== 'word' && nameToken.type !== 'quoted_ident')
99
+ break;
100
+ const afterName = nextSi + 1 < semIdx.length ? tokens[semIdx[nextSi + 1]] : null;
101
+ if (!afterName || afterName.type !== 'word' || afterName.value.toUpperCase() !== 'AS') {
102
+ break;
103
+ }
104
+ refs.push({ name: identName(nameToken), context: 'with' });
105
+ // Skip past AS and the parenthesized body
106
+ const bodyStart = nextSi + 2;
107
+ if (bodyStart < semIdx.length &&
108
+ tokens[semIdx[bodyStart]].type === 'punctuation' &&
109
+ tokens[semIdx[bodyStart]].value === '(') {
110
+ // Track paren depth
111
+ let depth = 1;
112
+ let k = bodyStart + 1;
113
+ while (k < semIdx.length && depth > 0) {
114
+ const t = tokens[semIdx[k]];
115
+ if (t.type === 'punctuation' && t.value === '(')
116
+ depth++;
117
+ if (t.type === 'punctuation' && t.value === ')')
118
+ depth--;
119
+ k++;
120
+ }
121
+ nextSi = k;
122
+ // Check for comma → next CTE
123
+ if (nextSi < semIdx.length &&
124
+ tokens[semIdx[nextSi]].type === 'punctuation' &&
125
+ tokens[semIdx[nextSi]].value === ',') {
126
+ nextSi++;
127
+ continue;
128
+ }
129
+ }
130
+ else {
131
+ break;
132
+ }
133
+ break;
134
+ }
135
+ continue;
136
+ }
137
+ // FROM <table> [, <table>, ...]
138
+ if (upper === 'FROM') {
139
+ let nextSi = si + 1;
140
+ if (nextSi >= semIdx.length)
141
+ continue;
142
+ const firstToken = tokens[semIdx[nextSi]];
143
+ // Skip subquery: FROM (SELECT ...)
144
+ if (firstToken.type === 'punctuation' && firstToken.value === '(')
145
+ continue;
146
+ if (firstToken.type === 'word' || firstToken.type === 'quoted_ident') {
147
+ // Check if function call: FROM generate_series(...)
148
+ const afterFirst = nextSi + 1 < semIdx.length ? tokens[semIdx[nextSi + 1]] : null;
149
+ if (afterFirst && afterFirst.type === 'punctuation' && afterFirst.value === '(') {
150
+ // Function call — skip
151
+ }
152
+ else {
153
+ refs.push({ name: identName(firstToken), context: 'from' });
154
+ }
155
+ nextSi++;
156
+ // Skip optional alias: AS alias, or just alias (word not a clause keyword)
157
+ nextSi = skipAlias(tokens, semIdx, nextSi);
158
+ // Check for comma-separated tables
159
+ while (nextSi < semIdx.length) {
160
+ const commaCheck = tokens[semIdx[nextSi]];
161
+ if (commaCheck.type !== 'punctuation' || commaCheck.value !== ',')
162
+ break;
163
+ nextSi++; // skip comma
164
+ if (nextSi >= semIdx.length)
165
+ break;
166
+ const nextTable = tokens[semIdx[nextSi]];
167
+ if (nextTable.type === 'word' || nextTable.type === 'quoted_ident') {
168
+ // Check for function call
169
+ const afterNext = nextSi + 1 < semIdx.length ? tokens[semIdx[nextSi + 1]] : null;
170
+ if (afterNext && afterNext.type === 'punctuation' && afterNext.value === '(') {
171
+ // Function call — skip
172
+ }
173
+ else {
174
+ refs.push({ name: identName(nextTable), context: 'from' });
175
+ }
176
+ nextSi++;
177
+ // Skip optional alias after comma-separated table
178
+ nextSi = skipAlias(tokens, semIdx, nextSi);
179
+ }
180
+ else {
181
+ nextSi++;
182
+ }
183
+ }
184
+ }
185
+ continue;
186
+ }
187
+ // JOIN <table> (possibly preceded by LEFT/RIGHT/INNER/OUTER/CROSS/FULL/NATURAL)
188
+ if (upper === 'JOIN') {
189
+ const nextSi = si + 1;
190
+ if (nextSi >= semIdx.length)
191
+ continue;
192
+ const joinTarget = tokens[semIdx[nextSi]];
193
+ // Skip subquery: JOIN (SELECT ...)
194
+ if (joinTarget.type === 'punctuation' && joinTarget.value === '(')
195
+ continue;
196
+ if (joinTarget.type === 'word' || joinTarget.type === 'quoted_ident') {
197
+ refs.push({ name: identName(joinTarget), context: 'join' });
198
+ }
199
+ }
200
+ }
201
+ return refs;
202
+ }
203
+ //# sourceMappingURL=sql-table-refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-table-refs.js","sourceRoot":"","sources":["../../src/logical/sql-table-refs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * クォート認識 SQL トークナイザー。
3
+ * 文字列リテラル・コメント内のキーワードやプレースホルダを安全に無視する基盤層。
4
+ */
5
+ export type SqlTokenType = 'word' | 'quoted_ident' | 'string' | 'number' | 'placeholder' | 'operator' | 'punctuation' | 'whitespace' | 'comment' | 'other';
6
+ export interface SqlToken {
7
+ type: SqlTokenType;
8
+ value: string;
9
+ offset: number;
10
+ }
11
+ /** クォート認識 SQL トークナイザー(状態機械、O(n) 単一パス) */
12
+ export declare function tokenizeSql(sql: string): SqlToken[];
13
+ /** $N placeholder のみ変換(文字列リテラル・コメント内は無視) */
14
+ export declare function offsetSqlParamsSafe(sql: string, offset: number): string;
15
+ /** クォート認識でプレースホルダ位置を分割(buildParameterizedSql の安全版) */
16
+ export declare function splitByPlaceholders(sql: string): {
17
+ parts: string[];
18
+ placeholderCount: number;
19
+ };
20
+ //# sourceMappingURL=sql-tokenizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-tokenizer.d.ts","sourceRoot":"","sources":["../../src/logical/sql-tokenizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,YAAY,GACrB,MAAM,GACN,cAAc,GACd,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,UAAU,GACV,aAAa,GACb,YAAY,GACZ,SAAS,GACT,OAAO,CAAC;AAEX,MAAM,WAAW,QAAQ;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AA4BD,yCAAyC;AACzC,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAsLnD;AAED,4CAA4C;AAC5C,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAavE;AAED,sDAAsD;AACtD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG;IACjD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CACzB,CAkBA"}
@@ -0,0 +1,249 @@
1
+ /**
2
+ * クォート認識 SQL トークナイザー。
3
+ * 文字列リテラル・コメント内のキーワードやプレースホルダを安全に無視する基盤層。
4
+ */
5
+ // Character classification helpers (avoid regex in hot loop)
6
+ function isWhitespace(c) {
7
+ return c === ' ' || c === '\t' || c === '\n' || c === '\r' || c === '\f';
8
+ }
9
+ function isWordStart(c) {
10
+ const code = c.charCodeAt(0);
11
+ return ((code >= 65 && code <= 90) || // A-Z
12
+ (code >= 97 && code <= 122) || // a-z
13
+ code === 95 // _
14
+ );
15
+ }
16
+ function isWordChar(c) {
17
+ const code = c.charCodeAt(0);
18
+ return ((code >= 65 && code <= 90) || // A-Z
19
+ (code >= 97 && code <= 122) || // a-z
20
+ (code >= 48 && code <= 57) || // 0-9
21
+ code === 95 // _
22
+ );
23
+ }
24
+ function isDigit(c) {
25
+ const code = c.charCodeAt(0);
26
+ return code >= 48 && code <= 57;
27
+ }
28
+ /** クォート認識 SQL トークナイザー(状態機械、O(n) 単一パス) */
29
+ export function tokenizeSql(sql) {
30
+ const tokens = [];
31
+ const len = sql.length;
32
+ let i = 0;
33
+ while (i < len) {
34
+ const ch = sql[i];
35
+ // Single-quoted string: 'text', '' escape
36
+ if (ch === "'") {
37
+ const start = i;
38
+ i++; // skip opening '
39
+ while (i < len) {
40
+ if (sql[i] === "'") {
41
+ if (i + 1 < len && sql[i + 1] === "'") {
42
+ i += 2; // '' escape
43
+ }
44
+ else {
45
+ i++; // closing '
46
+ break;
47
+ }
48
+ }
49
+ else {
50
+ i++;
51
+ }
52
+ }
53
+ tokens.push({ type: 'string', value: sql.slice(start, i), offset: start });
54
+ continue;
55
+ }
56
+ // Double-quoted identifier: "ident", "" escape
57
+ if (ch === '"') {
58
+ const start = i;
59
+ i++; // skip opening "
60
+ while (i < len) {
61
+ if (sql[i] === '"') {
62
+ if (i + 1 < len && sql[i + 1] === '"') {
63
+ i += 2; // "" escape
64
+ }
65
+ else {
66
+ i++; // closing "
67
+ break;
68
+ }
69
+ }
70
+ else {
71
+ i++;
72
+ }
73
+ }
74
+ tokens.push({ type: 'quoted_ident', value: sql.slice(start, i), offset: start });
75
+ continue;
76
+ }
77
+ // $ — dollar-quote or placeholder
78
+ if (ch === '$') {
79
+ // $N placeholder: $ followed by digit(s)
80
+ if (i + 1 < len && isDigit(sql[i + 1])) {
81
+ const start = i;
82
+ i++; // skip $
83
+ while (i < len && isDigit(sql[i]))
84
+ i++;
85
+ tokens.push({ type: 'placeholder', value: sql.slice(start, i), offset: start });
86
+ continue;
87
+ }
88
+ // Dollar-quoted string: $$...$$ or $tag$...$tag$
89
+ const tagStart = i;
90
+ i++; // skip first $
91
+ // Read tag (may be empty for $$)
92
+ let tag = '';
93
+ if (i < len && sql[i] === '$') {
94
+ // empty tag: $$
95
+ i++; // skip second $
96
+ }
97
+ else if (i < len && isWordStart(sql[i])) {
98
+ const tagNameStart = i;
99
+ while (i < len && isWordChar(sql[i]))
100
+ i++;
101
+ if (i < len && sql[i] === '$') {
102
+ tag = sql.slice(tagNameStart, i);
103
+ i++; // skip closing $ of tag
104
+ }
105
+ else {
106
+ // Not a valid dollar quote — emit as 'other'
107
+ tokens.push({ type: 'other', value: sql.slice(tagStart, i), offset: tagStart });
108
+ continue;
109
+ }
110
+ }
111
+ else {
112
+ // Just a lone $ — emit as 'other'
113
+ tokens.push({ type: 'other', value: '$', offset: tagStart });
114
+ continue;
115
+ }
116
+ // Now consume until closing $tag$
117
+ const closer = `$${tag}$`;
118
+ const closerIdx = sql.indexOf(closer, i);
119
+ if (closerIdx === -1) {
120
+ // Unclosed — consume rest
121
+ tokens.push({ type: 'string', value: sql.slice(tagStart), offset: tagStart });
122
+ i = len;
123
+ }
124
+ else {
125
+ i = closerIdx + closer.length;
126
+ tokens.push({ type: 'string', value: sql.slice(tagStart, i), offset: tagStart });
127
+ }
128
+ continue;
129
+ }
130
+ // Line comment: --
131
+ if (ch === '-' && i + 1 < len && sql[i + 1] === '-') {
132
+ const start = i;
133
+ i += 2;
134
+ while (i < len && sql[i] !== '\n')
135
+ i++;
136
+ if (i < len)
137
+ i++; // include \n
138
+ tokens.push({ type: 'comment', value: sql.slice(start, i), offset: start });
139
+ continue;
140
+ }
141
+ // Block comment: /* ... */
142
+ if (ch === '/' && i + 1 < len && sql[i + 1] === '*') {
143
+ const start = i;
144
+ i += 2;
145
+ while (i < len) {
146
+ if (sql[i] === '*' && i + 1 < len && sql[i + 1] === '/') {
147
+ i += 2;
148
+ break;
149
+ }
150
+ i++;
151
+ }
152
+ tokens.push({ type: 'comment', value: sql.slice(start, i), offset: start });
153
+ continue;
154
+ }
155
+ // ? placeholder
156
+ if (ch === '?') {
157
+ tokens.push({ type: 'placeholder', value: '?', offset: i });
158
+ i++;
159
+ continue;
160
+ }
161
+ // Whitespace
162
+ if (isWhitespace(ch)) {
163
+ const start = i;
164
+ while (i < len && isWhitespace(sql[i]))
165
+ i++;
166
+ tokens.push({ type: 'whitespace', value: sql.slice(start, i), offset: start });
167
+ continue;
168
+ }
169
+ // Word: keyword or identifier
170
+ if (isWordStart(ch)) {
171
+ const start = i;
172
+ while (i < len && isWordChar(sql[i]))
173
+ i++;
174
+ tokens.push({ type: 'word', value: sql.slice(start, i), offset: start });
175
+ continue;
176
+ }
177
+ // Number
178
+ if (isDigit(ch)) {
179
+ const start = i;
180
+ while (i < len && (isDigit(sql[i]) || sql[i] === '.'))
181
+ i++;
182
+ tokens.push({ type: 'number', value: sql.slice(start, i), offset: start });
183
+ continue;
184
+ }
185
+ // Punctuation
186
+ if (ch === '(' || ch === ')' || ch === ',' || ch === ';' || ch === '*') {
187
+ tokens.push({ type: 'punctuation', value: ch, offset: i });
188
+ i++;
189
+ continue;
190
+ }
191
+ // Multi-char operators
192
+ if ('<>=!|&+-/%^~'.includes(ch)) {
193
+ const start = i;
194
+ i++;
195
+ // Consume second char for two-char operators like <=, >=, <>, !=, ||, ::
196
+ if (i < len && '<>=!|:'.includes(sql[i]))
197
+ i++;
198
+ tokens.push({ type: 'operator', value: sql.slice(start, i), offset: start });
199
+ continue;
200
+ }
201
+ // :: cast operator
202
+ if (ch === ':' && i + 1 < len && sql[i + 1] === ':') {
203
+ tokens.push({ type: 'operator', value: '::', offset: i });
204
+ i += 2;
205
+ continue;
206
+ }
207
+ // Other single char (includes ., :, etc.)
208
+ tokens.push({ type: 'other', value: ch, offset: i });
209
+ i++;
210
+ }
211
+ return tokens;
212
+ }
213
+ /** $N placeholder のみ変換(文字列リテラル・コメント内は無視) */
214
+ export function offsetSqlParamsSafe(sql, offset) {
215
+ if (offset === 0)
216
+ return sql;
217
+ const tokens = tokenizeSql(sql);
218
+ const parts = [];
219
+ for (const token of tokens) {
220
+ if (token.type === 'placeholder' && token.value.startsWith('$')) {
221
+ const num = Number.parseInt(token.value.slice(1), 10);
222
+ parts.push(`$${num + offset}`);
223
+ }
224
+ else {
225
+ parts.push(token.value);
226
+ }
227
+ }
228
+ return parts.join('');
229
+ }
230
+ /** クォート認識でプレースホルダ位置を分割(buildParameterizedSql の安全版) */
231
+ export function splitByPlaceholders(sql) {
232
+ const tokens = tokenizeSql(sql);
233
+ const parts = [];
234
+ let current = '';
235
+ let placeholderCount = 0;
236
+ for (const token of tokens) {
237
+ if (token.type === 'placeholder') {
238
+ parts.push(current);
239
+ current = '';
240
+ placeholderCount++;
241
+ }
242
+ else {
243
+ current += token.value;
244
+ }
245
+ }
246
+ parts.push(current);
247
+ return { parts, placeholderCount };
248
+ }
249
+ //# sourceMappingURL=sql-tokenizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-tokenizer.js","sourceRoot":"","sources":["../../src/logical/sql-tokenizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /** JSONB 型判定・値変換ユーティリティ(純粋関数) */
2
+ export declare function isNumericType(type: string): boolean;
3
+ export declare function isBooleanType(type: string): boolean;
4
+ /** 論理カラム型に対応する PostgreSQL キャスト式を返す。TEXT 系はキャスト不要。 */
5
+ export declare function pgCastForType(type: string): string;
6
+ /**
7
+ * 文字列値を JSONB @> 比較に適した型に変換する。
8
+ * 変換失敗時は元の値をそのまま返す(安全側倒し)。
9
+ */
10
+ export declare function coerceValueForJsonb(value: unknown, columnType: string): unknown;
11
+ //# sourceMappingURL=type-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-utils.d.ts","sourceRoot":"","sources":["../../src/logical/type-utils.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKnD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAiB/E"}
@@ -0,0 +1,42 @@
1
+ /** JSONB 型判定・値変換ユーティリティ(純粋関数) */
2
+ export function isNumericType(type) {
3
+ const upper = type.toUpperCase();
4
+ return ['INTEGER', 'INT', 'BIGINT', 'REAL', 'FLOAT', 'NUMERIC', 'DECIMAL', 'DOUBLE'].some((t) => upper.includes(t));
5
+ }
6
+ export function isBooleanType(type) {
7
+ const upper = type.toUpperCase();
8
+ return upper === 'BOOLEAN' || upper === 'BOOL';
9
+ }
10
+ /** 論理カラム型に対応する PostgreSQL キャスト式を返す。TEXT 系はキャスト不要。 */
11
+ export function pgCastForType(type) {
12
+ if (isNumericType(type))
13
+ return '::numeric';
14
+ if (isBooleanType(type))
15
+ return '::boolean';
16
+ return '';
17
+ }
18
+ /**
19
+ * 文字列値を JSONB @> 比較に適した型に変換する。
20
+ * 変換失敗時は元の値をそのまま返す(安全側倒し)。
21
+ */
22
+ export function coerceValueForJsonb(value, columnType) {
23
+ if (value === null || value === undefined)
24
+ return value;
25
+ if (typeof value !== 'string')
26
+ return value;
27
+ if (isNumericType(columnType)) {
28
+ if (value.trim() === '')
29
+ return value;
30
+ const n = Number(value);
31
+ return Number.isNaN(n) || !Number.isFinite(n) ? value : n;
32
+ }
33
+ if (isBooleanType(columnType)) {
34
+ if (value === 'true')
35
+ return true;
36
+ if (value === 'false')
37
+ return false;
38
+ return value;
39
+ }
40
+ return value;
41
+ }
42
+ //# sourceMappingURL=type-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-utils.js","sourceRoot":"","sources":["../../src/logical/type-utils.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/F,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,MAAM,CAAC;AAChD,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,IAAI,aAAa,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC;IAC5C,IAAI,aAAa,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC;IAC5C,OAAO,EAAE,CAAC;AACX,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAc,EAAE,UAAkB;IACrE,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAE5C,IAAI,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,KAAK,CAAC;QACtC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACxB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,IAAI,KAAK,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QAClC,IAAI,KAAK,KAAK,OAAO;YAAE,OAAO,KAAK,CAAC;QACpC,OAAO,KAAK,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** WHERE {key: value} → JSONB 条件配列(NULL-safe、型変換付き) */
2
+ export declare function buildWhereConditions(where: Record<string, unknown>, columns?: Array<{
3
+ name: string;
4
+ type: string;
5
+ }>): import("drizzle-orm").SQL<unknown>[];
6
+ //# sourceMappingURL=where-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"where-builder.d.ts","sourceRoot":"","sources":["../../src/logical/where-builder.ts"],"names":[],"mappings":"AAKA,uDAAuD;AACvD,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,wCAe/C"}
@@ -0,0 +1,20 @@
1
+ import { sql } from 'drizzle-orm';
2
+ import { isValidIdentifier } from 'viyv-db-core';
3
+ import { logicalRows } from '../schema.js';
4
+ import { coerceValueForJsonb } from './type-utils.js';
5
+ /** WHERE {key: value} → JSONB 条件配列(NULL-safe、型変換付き) */
6
+ export function buildWhereConditions(where, columns) {
7
+ const typeMap = columns ? new Map(columns.map((c) => [c.name, c.type])) : null;
8
+ return Object.entries(where).map(([key, value]) => {
9
+ if (!isValidIdentifier(key)) {
10
+ throw new Error(`Invalid column name in WHERE: "${key}"`);
11
+ }
12
+ if (value === null) {
13
+ return sql.raw(`(${logicalRows.data.name}->>'${key}') IS NULL`);
14
+ }
15
+ const colType = typeMap?.get(key);
16
+ const coerced = colType ? coerceValueForJsonb(value, colType) : value;
17
+ return sql `${logicalRows.data} @> ${JSON.stringify({ [key]: coerced })}::jsonb`;
18
+ });
19
+ }
20
+ //# sourceMappingURL=where-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"where-builder.js","sourceRoot":"","sources":["../../src/logical/where-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,uDAAuD;AACvD,MAAM,UAAU,oBAAoB,CACnC,KAA8B,EAC9B,OAA+C;IAE/C,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE/E,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACjD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,GAAG,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,OAAO,GAAG,YAAY,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACtE,OAAO,GAAG,CAAA,GAAG,WAAW,CAAC,IAAI,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC;IACjF,CAAC,CAAC,CAAC;AACJ,CAAC"}