@platforma-sdk/workflow-tengo 5.15.1 → 5.17.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 (47) hide show
  1. package/dist/tengo/lib/exec.lib.tengo +2 -0
  2. package/dist/tengo/lib/pframes.build-query.lib.tengo +128 -0
  3. package/dist/tengo/lib/pt.lib.tengo +139 -107
  4. package/dist/tengo/lib/pt.util.lib.tengo +225 -171
  5. package/dist/tengo/tpl/annotations.compute.plj.gz +0 -0
  6. package/dist/tengo/tpl/anonymize.deanonymize.plj.gz +0 -0
  7. package/dist/tengo/tpl/anonymize.impl.plj.gz +0 -0
  8. package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
  9. package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
  10. package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
  11. package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
  12. package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
  13. package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
  14. package/dist/tengo/tpl/check_network.workdir_type_name_extractor.plj.gz +0 -0
  15. package/dist/tengo/tpl/exec.conda.create-env.plj.gz +0 -0
  16. package/dist/tengo/tpl/exec.delayed-compute-allocation.impl.plj.gz +0 -0
  17. package/dist/tengo/tpl/exec.exec-impl.plj.gz +0 -0
  18. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  19. package/dist/tengo/tpl/exec.load-software.impl.plj.gz +0 -0
  20. package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
  21. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  22. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  23. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  24. package/dist/tengo/tpl/exec.python.venv-bin-path.plj.gz +0 -0
  25. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  26. package/dist/tengo/tpl/pframes.build-table.plj.gz +0 -0
  27. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  28. package/dist/tengo/tpl/pframes.export-parquet-pcolumn-chunk.plj.gz +0 -0
  29. package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
  30. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  31. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  32. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  33. package/dist/tengo/tpl/pframes.parse-to-json.plj.gz +0 -0
  34. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  35. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  36. package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
  37. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  38. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  39. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  40. package/dist/tengo/tpl/pt.import-dir.plj.gz +0 -0
  41. package/dist/tengo/tpl/pt.workflow-run.plj.gz +0 -0
  42. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  43. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  44. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  45. package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
  46. package/dist/tengo/tpl/workflow.query-context-filter.plj.gz +0 -0
  47. package/package.json +3 -3
@@ -1045,6 +1045,8 @@ builder := func() {
1045
1045
  ram: ramRequest
1046
1046
  }
1047
1047
  if !is_undefined(gpuMemory) {
1048
+ ll.assert(!is_undefined(cpuRequest), "exec.builder: cpu() is required when gpuMemory() is set. GPU nodes vary in CPU capacity and cost; please provide an explicit CPU request.")
1049
+ ll.assert(!is_undefined(ramRequest), "exec.builder: mem() is required when gpuMemory() is set. GPU nodes vary in RAM capacity and cost; please provide an explicit RAM request.")
1048
1050
  quotaData.gpuMemory = gpuMemory
1049
1051
  }
1050
1052
  metaExecInputs := {
@@ -0,0 +1,128 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+ ll := import("@platforma-sdk/workflow-tengo:ll")
15
+
16
+
17
+
18
+ columnEntry := func(columnId, qualifications) {
19
+ entry := { entry: { type: "column", column: columnId } }
20
+ if !is_undefined(qualifications) && len(qualifications) > 0 {
21
+ entry.qualifications = qualifications
22
+ }
23
+ return entry
24
+ }
25
+
26
+ linkerJoinEntry := func(linkerId, secondary, qualifications) {
27
+ entry := {
28
+ entry: {
29
+ type: "linkerJoin",
30
+ linker: { column: linkerId },
31
+ secondary: secondary
32
+ }
33
+ }
34
+ if !is_undefined(qualifications) && len(qualifications) > 0 {
35
+ entry.qualifications = qualifications
36
+ }
37
+ return entry
38
+ }
39
+
40
+ innerJoinEntry := func(entries, qualifications) {
41
+ entry := { entry: { type: "innerJoin", entries: entries } }
42
+ if !is_undefined(qualifications) && len(qualifications) > 0 {
43
+ entry.qualifications = qualifications
44
+ }
45
+ return entry
46
+ }
47
+
48
+ wrapLinker := func(currentEntry, linkerId) {
49
+ return linkerJoinEntry(linkerId, [currentEntry], [])
50
+ }
51
+
52
+ wrapFilter := func(currentEntry, filterId) {
53
+ return innerJoinEntry([currentEntry, columnEntry(filterId, [])], [])
54
+ }
55
+
56
+ stepColumnId := func(step) {
57
+ if step.type == "linker" {
58
+ ll.assert(!is_undefined(step.linker), "linker step missing 'linker' field")
59
+ ll.assert(!is_undefined(step.linker.columnId),
60
+ "linker step's linker missing 'columnId' field")
61
+ return step.linker.columnId
62
+ }
63
+ if step.type == "filter" {
64
+ ll.assert(!is_undefined(step.filter), "filter step missing 'filter' field")
65
+ ll.assert(!is_undefined(step.filter.columnId),
66
+ "filter step's filter missing 'columnId' field")
67
+ return step.filter.columnId
68
+ }
69
+ ll.panic("unknown step type: %v", step.type)
70
+ }
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+ buildQuery := func(input) {
83
+ ll.assert(ll.isMap(input), "buildQuery: input must be a map, got %v", input)
84
+ version := input.version
85
+ ll.assert(version == "v1",
86
+ "buildQuery: unsupported version %v (expected \"v1\")", version)
87
+
88
+ column := input.column
89
+ ll.assert(is_string(column),
90
+ "buildQuery: 'column' must be a string, got %v", column)
91
+
92
+ path := input.path
93
+ if is_undefined(path) {
94
+ path = []
95
+ }
96
+ ll.assert(is_array(path), "buildQuery: 'path' must be an array")
97
+
98
+ qualifications := input.qualifications
99
+ if is_undefined(qualifications) {
100
+ qualifications = []
101
+ }
102
+ ll.assert(is_array(qualifications),
103
+ "buildQuery: 'qualifications' must be an array")
104
+
105
+ current := columnEntry(column, [])
106
+ for i := len(path) - 1; i >= 0; i-- {
107
+ step := path[i]
108
+ colId := stepColumnId(step)
109
+ if step.type == "linker" {
110
+ current = wrapLinker(current, colId)
111
+ } else {
112
+ current = wrapFilter(current, colId)
113
+ }
114
+ }
115
+
116
+ if len(qualifications) == 0 {
117
+ return current
118
+ }
119
+ current.qualifications = qualifications
120
+ return current
121
+ }
122
+
123
+ export ll.toStrict({
124
+ buildQuery: buildQuery,
125
+ columnEntry: columnEntry,
126
+ linkerJoinEntry: linkerJoinEntry,
127
+ innerJoinEntry: innerJoinEntry
128
+ })
@@ -59,33 +59,104 @@ _newDataFrameGroupBy := undefined
59
59
 
60
60
  workflowCounter := 0
61
61
 
62
- _validateColumn := func(name, columnStruct) {
62
+ _validateColumn := func(name, columnMap) {
63
63
  ll.assert(is_string(name), "column name must be a string")
64
- ll.assert(is_map(columnStruct), "columnStruct must be a map")
65
- ll.assert(!is_undefined(columnStruct.spec), "columnStruct must have 'spec' field defined")
66
- ll.assert(!is_undefined(columnStruct.data), "columnStruct must have 'data' field defined")
64
+ ll.assert(is_map(columnMap), "column must be a map")
65
+ ll.assert(!is_undefined(columnMap.spec), "column must have 'spec' field defined")
66
+ ll.assert(!is_undefined(columnMap.data), "column must have 'data' field defined")
67
67
  }
68
68
 
69
- _newPEntry := func(columnsByName, joinTree) {
69
+ _newPEntry := func(columnsByName, specQuery) {
70
70
  return ll.toStrict({
71
71
 
72
72
  _getColumnsMap: func() {
73
73
  return columnsByName
74
74
  },
75
75
 
76
- _getJoinTree: func() {
77
- return joinTree
76
+ _getSpecQuery: func() {
77
+ return specQuery
78
78
  }
79
79
  })
80
80
  }
81
81
 
82
82
  _isPEntry := func(entry) {
83
- return ll.isMap(entry) && ll.methodExists(entry, "_getColumnsMap") && ll.methodExists(entry, "_getJoinTree");
83
+ return ll.isMap(entry) && ll.methodExists(entry, "_getColumnsMap") && ll.methodExists(entry, "_getSpecQuery");
84
84
  }
85
85
 
86
86
  _frameToPEntries := undefined
87
87
 
88
- slicedColumnCounter := 0
88
+
89
+
90
+
91
+
92
+ _mergePEntries := func(entries) {
93
+ columns := {}
94
+ specQueries := []
95
+ for entry in entries {
96
+ ll.assert(_isPEntry(entry), "expected a PEntry, got: %v", entry)
97
+ entryColumns := entry._getColumnsMap()
98
+
99
+
100
+ maps.forEach(entryColumns, func(name, info) {
101
+ if !is_undefined(columns[name]) {
102
+ ll.panic("column name %s is already used, please pick a different name", name)
103
+ }
104
+ columns[name] = info
105
+ })
106
+ specQueries = append(specQueries, entry._getSpecQuery())
107
+ }
108
+ return { columns: columns, specQueries: specQueries }
109
+ }
110
+
111
+
112
+
113
+
114
+ _asJoinEntry := func(specQuery) {
115
+ return { entry: specQuery }
116
+ }
117
+
118
+ _asJoinEntries := func(specQueries) {
119
+ return slices.map(specQueries, _asJoinEntry)
120
+ }
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+ _buildAxisSelector := func(axesSpec, idx) {
129
+ axis := axesSpec[idx]
130
+ selector := { name: axis.name }
131
+ if !is_undefined(axis.type) {
132
+ selector.type = axis.type
133
+ }
134
+ if !is_undefined(axis.domain) && len(axis.domain) > 0 {
135
+ selector.domain = maps.clone(axis.domain)
136
+ }
137
+ if !is_undefined(axis.contextDomain) && len(axis.contextDomain) > 0 {
138
+ selector.contextDomain = maps.clone(axis.contextDomain)
139
+ }
140
+ if !is_undefined(axis.parentAxes) && len(axis.parentAxes) > 0 {
141
+ parents := []
142
+ for parentIdx in axis.parentAxes {
143
+ ll.assert(parentIdx >= 0 && parentIdx < idx,
144
+ "slicedColumn: parentAxes index %d must be less than current axis index %d",
145
+ parentIdx, idx)
146
+ parents = append(parents, _buildAxisSelector(axesSpec, parentIdx))
147
+ }
148
+ selector.parentAxes = parents
149
+ }
150
+ return selector
151
+ }
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
89
160
 
90
161
  p := ll.toStrict({
91
162
 
@@ -94,13 +165,13 @@ p := ll.toStrict({
94
165
 
95
166
 
96
167
 
97
- column: func(name, columnStruct) {
98
- _validateColumn(name, columnStruct)
168
+ column: func(name, columnMap) {
169
+ _validateColumn(name, columnMap)
99
170
  columnsByName := {}
100
- columnsByName[name] = columnStruct
171
+ columnsByName[name] = columnMap
101
172
  return _newPEntry(columnsByName, {
102
173
  type: "column",
103
- columnId: name
174
+ column: name
104
175
  })
105
176
  },
106
177
 
@@ -110,20 +181,35 @@ p := ll.toStrict({
110
181
 
111
182
 
112
183
 
184
+
185
+
186
+
113
187
  slicedColumn: func(name, columnMap, axisFilters) {
114
188
  _validateColumn(name, columnMap)
115
189
  util.validateAxisFilters(axisFilters)
116
190
 
117
- slicedColumnCounter += 1
118
- oldName := "p_sliced_" + slicedColumnCounter
191
+ axesSpec := columnMap.spec.axesSpec
192
+ ll.assert(is_array(axesSpec),
193
+ "slicedColumn: column %q spec must carry axesSpec, got: %v", name, columnMap.spec)
194
+
195
+ resolvedFilters := []
196
+ for filter in axisFilters {
197
+ idx := filter.axisIndex
198
+ ll.assert(idx >= 0 && idx < len(axesSpec),
199
+ "slicedColumn: axisIndex %d out of range for column %q (axes: %d)",
200
+ idx, name, len(axesSpec))
201
+ resolvedFilters = append(resolvedFilters, {
202
+ axisSelector: _buildAxisSelector(axesSpec, idx),
203
+ constant: filter.constant
204
+ })
205
+ }
119
206
 
120
207
  columnsByName := {}
121
- columnsByName[oldName] = columnMap
208
+ columnsByName[name] = columnMap
122
209
  return _newPEntry(columnsByName, {
123
- type: "slicedColumn",
124
- columnId: oldName,
125
- newId: name,
126
- axisFilters: axisFilters
210
+ type: "sliceAxes",
211
+ input: { type: "column", column: name },
212
+ axisFilters: resolvedFilters
127
213
  })
128
214
  },
129
215
 
@@ -133,36 +219,14 @@ p := ll.toStrict({
133
219
 
134
220
  inner: func(...entries) {
135
221
  ll.assert(len(entries) > 0, "inner join requires at least one entry")
136
-
137
222
  if len(entries) == 1 && !_isPEntry(entries[0]) {
138
223
  entries = _frameToPEntries(entries[0])
139
224
  }
140
-
141
- mergedColMap := {}
142
- joinTreeEntries := []
143
-
144
- for entry in entries {
145
- ll.assert(_isPEntry(entry), "all entries must be PEntry objects")
146
-
147
- entryColMap := entry._getColumnsMap()
148
- entryJoinTree := entry._getJoinTree()
149
-
150
- for colName, colInfo in entryColMap {
151
- if !is_undefined(mergedColMap[colName]) {
152
- ll.panic("column name %s is already used, please pick a different name", colName)
153
- }
154
- mergedColMap[colName] = colInfo
155
- }
156
-
157
- joinTreeEntries = append(joinTreeEntries, entryJoinTree)
158
- }
159
-
160
- joinTree := {
161
- type: "inner",
162
- entries: joinTreeEntries
163
- }
164
-
165
- return _newPEntry(mergedColMap, joinTree)
225
+ merged := _mergePEntries(entries)
226
+ return _newPEntry(merged.columns, {
227
+ type: "innerJoin",
228
+ entries: _asJoinEntries(merged.specQueries)
229
+ })
166
230
  },
167
231
 
168
232
 
@@ -171,36 +235,14 @@ p := ll.toStrict({
171
235
 
172
236
  full: func(...entries) {
173
237
  ll.assert(len(entries) > 0, "full join requires at least one entry")
174
-
175
238
  if len(entries) == 1 && !_isPEntry(entries[0]) {
176
239
  entries = _frameToPEntries(entries[0])
177
240
  }
178
-
179
- mergedColMap := {}
180
- joinTreeEntries := []
181
-
182
- for entry in entries {
183
- ll.assert(_isPEntry(entry), "all entries must be PEntry objects")
184
-
185
- entryColMap := entry._getColumnsMap()
186
- entryJoinTree := entry._getJoinTree()
187
-
188
- for colName, colInfo in entryColMap {
189
- if !is_undefined(mergedColMap[colName]) {
190
- ll.panic("column name %s is already used, please pick a different name", colName)
191
- }
192
- mergedColMap[colName] = colInfo
193
- }
194
-
195
- joinTreeEntries = append(joinTreeEntries, entryJoinTree)
196
- }
197
-
198
- joinTree := {
199
- type: "full",
200
- entries: joinTreeEntries
201
- }
202
-
203
- return _newPEntry(mergedColMap, joinTree)
241
+ merged := _mergePEntries(entries)
242
+ return _newPEntry(merged.columns, {
243
+ type: "fullJoin",
244
+ entries: _asJoinEntries(merged.specQueries)
245
+ })
204
246
  },
205
247
 
206
248
 
@@ -211,41 +253,37 @@ p := ll.toStrict({
211
253
  outer: func(primary, ...secondary) {
212
254
  ll.assert(_isPEntry(primary), "primary entry must be a PEntry object")
213
255
  ll.assert(len(secondary) > 0, "outer join requires at least one secondary entry")
256
+ merged := _mergePEntries(append([primary], secondary...))
257
+ return _newPEntry(merged.columns, {
258
+ type: "outerJoin",
259
+ primary: _asJoinEntry(merged.specQueries[0]),
260
+ secondary: _asJoinEntries(merged.specQueries[1:])
261
+ })
262
+ },
263
+
214
264
 
215
- mergedColMap := {}
216
265
 
217
- primaryColMap := primary._getColumnsMap()
218
- primaryJoinTree := primary._getJoinTree()
219
266
 
220
- for colName, colInfo in primaryColMap {
221
- mergedColMap[colName] = colInfo
222
- }
223
267
 
224
- secondaryJoinTrees := []
225
268
 
226
- for entry in secondary {
227
- ll.assert(_isPEntry(entry), "all secondary entries must be PEntry objects")
228
269
 
229
- entryColMap := entry._getColumnsMap()
230
- entryJoinTree := entry._getJoinTree()
231
270
 
232
- for colName, colInfo in entryColMap {
233
- if !is_undefined(mergedColMap[colName]) {
234
- ll.panic("column name %s is already used, please pick a different name", colName)
235
- }
236
- mergedColMap[colName] = colInfo
237
- }
238
271
 
239
- secondaryJoinTrees = append(secondaryJoinTrees, entryJoinTree)
240
- }
241
272
 
242
- joinTree := {
243
- type: "outer",
244
- primary: primaryJoinTree,
245
- secondary: secondaryJoinTrees
246
- }
247
273
 
248
- return _newPEntry(mergedColMap, joinTree)
274
+
275
+ linkerJoin: func(linkerColumnEntry, secondaryEntry) {
276
+ ll.assert(_isPEntry(linkerColumnEntry), "linker must be a PEntry object")
277
+ linkerQuery := linkerColumnEntry._getSpecQuery()
278
+ ll.assert(ll.isMap(linkerQuery) && linkerQuery.type == "column",
279
+ "linkerJoin: linker entry must be a plain p.column(...) result, got: %v", linkerQuery)
280
+
281
+ merged := _mergePEntries([linkerColumnEntry, secondaryEntry])
282
+ return _newPEntry(merged.columns, {
283
+ type: "linkerJoin",
284
+ linker: { column: linkerQuery.column },
285
+ secondary: [_asJoinEntry(merged.specQueries[1])]
286
+ })
249
287
  }
250
288
  })
251
289
 
@@ -556,7 +594,6 @@ workflow := func() {
556
594
  step := undefined
557
595
  if finalFormat == "pentry" {
558
596
  columnsMap := frameInput._getColumnsMap()
559
- src := frameInput._getJoinTree()
560
597
 
561
598
 
562
599
  translation := {}
@@ -571,12 +608,7 @@ workflow := func() {
571
608
  renamedColumnsMap[workflowUniqueColumnId] = columnInfo
572
609
  })
573
610
 
574
-
575
- request := {
576
- src: src,
577
- filters: []
578
- }
579
- renamedRequest := util.renameJoinColumns(request, rename)
611
+ renamedQuery := util.renameSpecQueryColumns(frameInput._getSpecQuery(), rename)
580
612
 
581
613
 
582
614
  ll.assert(is_undefined(inFrames[finalDataFrameId]),
@@ -588,7 +620,7 @@ workflow := func() {
588
620
  step = {
589
621
  type: "read_frame",
590
622
  name: finalDataFrameId,
591
- request: renamedRequest,
623
+ request: { query: renamedQuery },
592
624
  translation: translation
593
625
  }
594
626
  } else if finalFormat == "parquet" {
@@ -190,219 +190,273 @@ validateAxisFilters := func(axisFilter) {
190
190
  }
191
191
 
192
192
 
193
- _JOIN_ENTRY_SCHEMA := undefined
194
193
 
195
- _JOIN_LEAF_SCHEMA := func(js, _, _) {
196
- return validation.checkJson(js, _JOIN_ENTRY_SCHEMA)
197
- }
198
- _JOIN_SUBTREE_SCHEMA := func(js, _, _) {
199
- return validation.checkJson(js, [_JOIN_ENTRY_SCHEMA])
200
- }
201
-
202
- _JOIN_ENTRY_SCHEMA = [`or`,
203
- {
204
-
205
- `type`: `string,regex=column`,
206
-
207
- `columnId`: `string`
208
- },
209
- {
210
-
211
- `type`: `string,regex=slicedColumn`,
212
-
213
- `columnId`: `string`,
214
-
215
- `newId`: `string`,
216
-
217
- `axisFilters`: [_AXIS_FILTER_SCHEMA]
218
- },
219
- {
220
-
221
- `type`: `string,regex=artificialColumn`,
222
-
223
- `columnId`: `string`,
224
194
 
225
- `newId`: `string`,
226
195
 
227
- `axesIndices`: [`number`]
228
- },
229
- {
230
196
 
231
- `type`: `string,regex=inlineColumn`,
232
197
 
233
- `newId`: `string`,
234
-
235
- `spec`: spec.P_COLUMN_STRICT_SPEC_SCHEMA,
198
+ _SINGLE_AXIS_SELECTOR_SCHEMA := undefined
199
+ _PARENT_AXES_LAZY := func(js, _, _) {
200
+ return validation.checkJson(js, [_SINGLE_AXIS_SELECTOR_SCHEMA])
201
+ }
202
+ _SINGLE_AXIS_SELECTOR_SCHEMA = {
203
+ `name`: `string`,
204
+ `type,?`: `string,regex=Int|Long|String`,
205
+ `domain,?`: { any: `string` },
206
+ `contextDomain,?`: { any: `string` },
207
+ `parentAxes,?`: _PARENT_AXES_LAZY
208
+ }
236
209
 
237
- `dataInfo`: {
210
+ _SLICE_AXIS_FILTER_SCHEMA := {
211
+ `axisSelector`: _SINGLE_AXIS_SELECTOR_SCHEMA,
212
+ `constant`: [`or`, `number`, `string`]
213
+ }
238
214
 
239
- `type`: `string,regex=Json`,
240
215
 
241
- `keyLength`: `number`,
216
+ _SPEC_QUERY_SCHEMA := undefined
217
+ _SPEC_QUERY_JOIN_ENTRY_SCHEMA := undefined
218
+ _SPEC_EXPRESSION_SCHEMA := undefined
219
+ _SPEC_QUERY_LAZY := func(js, _, _) {
220
+ return validation.checkJson(js, _SPEC_QUERY_SCHEMA)
221
+ }
222
+ _SPEC_QUERY_JOIN_ENTRY_LAZY := func(js, _, _) {
223
+ return validation.checkJson(js, _SPEC_QUERY_JOIN_ENTRY_SCHEMA)
224
+ }
225
+ _SPEC_QUERY_JOIN_ENTRY_LIST_LAZY := func(js, _, _) {
226
+ return validation.checkJson(js, [_SPEC_QUERY_JOIN_ENTRY_SCHEMA])
227
+ }
228
+ _SPEC_EXPRESSION_LAZY := func(js, _, _) {
229
+ return validation.checkJson(js, _SPEC_EXPRESSION_SCHEMA)
230
+ }
231
+ _SPEC_EXPRESSION_LIST_LAZY := func(js, _, _) {
232
+ return validation.checkJson(js, [_SPEC_EXPRESSION_SCHEMA])
233
+ }
242
234
 
243
- `data`: {
244
- any: [`or`, `null`, `number`, `string`]
245
- }
246
- }
247
- },
235
+ _SPEC_EXPRESSION_SCHEMA = [`or`,
236
+ { `type`: `string,regex=columnRef`, `value`: `string` },
237
+ { `type`: `string,regex=axisRef`, `value`: _SINGLE_AXIS_SELECTOR_SCHEMA },
238
+ { `type`: `string,regex=constant`, `value`: [`or`, `string`, `number`, `bool`] },
248
239
  {
249
-
250
- `type`: `string,regex=inner`,
251
-
252
- `entries`: _JOIN_SUBTREE_SCHEMA
240
+ `type`: `string,regex=numericUnary`,
241
+ `operand`: `string,regex=abs|ceil|floor|round|sqrt|log|log2|log10|exp|negate`,
242
+ `input`: _SPEC_EXPRESSION_LAZY
253
243
  },
254
244
  {
255
-
256
- `type`: `string,regex=full`,
257
-
258
- `entries`: _JOIN_SUBTREE_SCHEMA
245
+ `type`: `string,regex=numericBinary`,
246
+ `operand`: `string,regex=add|sub|mul|div|mod`,
247
+ `left`: _SPEC_EXPRESSION_LAZY,
248
+ `right`: _SPEC_EXPRESSION_LAZY
259
249
  },
260
250
  {
261
-
262
- `type`: `string,regex=outer`,
263
-
264
- `primary`: _JOIN_LEAF_SCHEMA,
265
-
266
-
267
- `secondary`: _JOIN_SUBTREE_SCHEMA
251
+ `type`: `string,regex=numericComparison`,
252
+ `operand`: `string,regex=eq|ne|lt|le|gt|ge`,
253
+ `left`: _SPEC_EXPRESSION_LAZY,
254
+ `right`: _SPEC_EXPRESSION_LAZY
255
+ },
256
+ {
257
+ `type`: `string,regex=stringEquals`,
258
+ `input`: _SPEC_EXPRESSION_LAZY,
259
+ `value`: `string`,
260
+ `caseInsensitive`: `bool`
261
+ },
262
+ {
263
+ `type`: `string,regex=stringContains`,
264
+ `input`: _SPEC_EXPRESSION_LAZY,
265
+ `value`: `string`,
266
+ `caseInsensitive`: `bool`
267
+ },
268
+ {
269
+ `type`: `string,regex=stringRegex`,
270
+ `input`: _SPEC_EXPRESSION_LAZY,
271
+ `value`: `string`
272
+ },
273
+ {
274
+ `type`: `string,regex=stringContainsFuzzy`,
275
+ `input`: _SPEC_EXPRESSION_LAZY,
276
+ `value`: `string`,
277
+ `maxEdits`: `number`,
278
+ `caseInsensitive`: `bool`,
279
+ `substitutionsOnly`: `bool`,
280
+ `wildcard,?`: [`or`, `null`, `string`]
281
+ },
282
+ { `type`: `string,regex=isNull`, `input`: _SPEC_EXPRESSION_LAZY },
283
+ {
284
+ `type`: `string,regex=ifNull`,
285
+ `input`: _SPEC_EXPRESSION_LAZY,
286
+ `replacement`: _SPEC_EXPRESSION_LAZY
287
+ },
288
+ { `type`: `string,regex=not`, `input`: _SPEC_EXPRESSION_LAZY },
289
+ { `type`: `string,regex=and|or`, `input`: _SPEC_EXPRESSION_LIST_LAZY },
290
+ {
291
+ `type`: `string,regex=isIn`,
292
+ `input`: _SPEC_EXPRESSION_LAZY,
293
+ `set`: [`or`, [`string`], [`number`]],
294
+ `negate,?`: `bool`
268
295
  }]
269
296
 
270
-
271
- _PREDICATE_SCHEMA := undefined
272
-
273
- _PREDICATE_LEAF_SCHEMA := func(js, _, _) {
274
- return validation.checkJson(js, _PREDICATE_SCHEMA)
275
- }
276
- _PREDICATE_SUBTREE_SCHEMA := func(js, _, _) {
277
- return validation.checkJson(js, [_PREDICATE_SCHEMA])
278
- }
279
- _PREDICATE_SCHEMA = [`or`,
280
- {
281
-
282
- `operator`: `string,regex=IsNA`
297
+ _SPEC_QUERY_SCHEMA = [`or`,
298
+ {
299
+ `type`: `string,regex=column`,
300
+ `column`: `string`
283
301
  },
284
- {
285
-
286
- `operator`: `string,regex=Equal|Less|LessOrEqual|Greater|GreaterOrEqual`,
287
-
288
- `reference`: [`or`, `number`, `string`]
302
+ {
303
+ `type`: `string,regex=sliceAxes`,
304
+ `input`: _SPEC_QUERY_LAZY,
305
+ `axisFilters`: [_SLICE_AXIS_FILTER_SCHEMA]
289
306
  },
290
- {
291
-
292
- `operator`: `string,regex=InSet`,
293
-
294
- `values`: [`or`, [`number`], [`string`]]
295
- },
296
- {
297
-
298
- `operator`: `string,regex=IEqual`,
299
-
300
- `reference`: `string`
307
+ {
308
+ `type`: `string,regex=innerJoin|fullJoin`,
309
+ `entries`: _SPEC_QUERY_JOIN_ENTRY_LIST_LAZY
301
310
  },
302
- {
303
-
304
- `operator`: `string,regex=StringContains|StringIContains`,
305
-
306
- `substring`: `string`
311
+ {
312
+ `type`: `string,regex=outerJoin`,
313
+ `primary`: _SPEC_QUERY_JOIN_ENTRY_LAZY,
314
+ `secondary`: _SPEC_QUERY_JOIN_ENTRY_LIST_LAZY
307
315
  },
308
- {
309
-
310
- `operator`: `string,regex=Matches`,
311
-
312
- `regex`: `string`
316
+ {
317
+ `type`: `string,regex=linkerJoin`,
318
+ `linker`: { `column`: `string` },
319
+ `secondary`: _SPEC_QUERY_JOIN_ENTRY_LIST_LAZY
313
320
  },
314
- {
315
-
316
- `operator`: `string,regex=StringContainsFuzzy|StringIContainsFuzzy`,
317
-
318
- `reference`: `string`,
319
-
320
- `maxEdits`: `number`,
321
-
322
- `wildcard,?`: `string`,
321
+ {
322
+ `type`: `string,regex=filter`,
323
+ `input`: _SPEC_QUERY_LAZY,
324
+ `predicate`: _SPEC_EXPRESSION_SCHEMA
325
+ }]
323
326
 
324
- `substitutionsOnly,?`: `bool`
325
- },
326
- {
327
+ _SPEC_QUERY_JOIN_ENTRY_SCHEMA = {
328
+ `entry`: _SPEC_QUERY_SCHEMA,
329
+ `qualifications,?`: [{
330
+ `axis`: _SINGLE_AXIS_SELECTOR_SCHEMA,
331
+ `contextDomain`: { any: `string` }
332
+ }]
333
+ }
327
334
 
328
- `operator`: `string,regex=Not`,
335
+ _renameSpecQuery := undefined
336
+ _renameExpression := undefined
329
337
 
330
- `operand`: _PREDICATE_LEAF_SCHEMA
331
- },
332
- {
333
338
 
334
- `operator`: `string,regex=And|Or`,
335
339
 
336
- `operands`: [_PREDICATE_SUBTREE_SCHEMA]
337
- }]
340
+ _renameJoinEntry := func(joinEntry, rename) {
341
+ result := { entry: _renameSpecQuery(joinEntry.entry, rename) }
342
+ if !is_undefined(joinEntry.qualifications) && len(joinEntry.qualifications) > 0 {
343
+ result.qualifications = joinEntry.qualifications
344
+ }
345
+ return result
346
+ }
338
347
 
339
- _P_TABLE_COLUMN_ID_SCHEMA := [`or`,
340
- {
348
+ _renameJoinEntries := func(entries, rename) {
349
+ return slices.map(entries, func(e) { return _renameJoinEntry(e, rename) })
350
+ }
341
351
 
342
- `type`: `string,regex=column`,
352
+ _renameExpressions := func(exprs, rename) {
353
+ return slices.map(exprs, func(e) { return _renameExpression(e, rename) })
354
+ }
343
355
 
344
- `id`: `string`
345
- },
346
- {
347
356
 
348
- `type`: `string,regex=axis`,
349
357
 
350
- `axisId`: {
351
- `name`: `string`,
352
- `type`: `string,regex=Int|Long|String`,
353
- `domain,?`: { any: `string` },
354
- `contextDomain,?`: { any: `string` }
358
+ _renameExpression = func(expr, rename) {
359
+ t := expr.type
360
+ if t == "columnRef" {
361
+ return { type: "columnRef", value: rename[expr.value] }
362
+ } else if t == "axisRef" || t == "constant" {
363
+ return expr
364
+ } else if t == "numericUnary" {
365
+ return { type: t, operand: expr.operand, input: _renameExpression(expr.input, rename) }
366
+ } else if t == "numericBinary" || t == "numericComparison" {
367
+ return {
368
+ type: t, operand: expr.operand,
369
+ left: _renameExpression(expr.left, rename),
370
+ right: _renameExpression(expr.right, rename)
371
+ }
372
+ } else if t == "stringEquals" || t == "stringContains" {
373
+ return {
374
+ type: t,
375
+ input: _renameExpression(expr.input, rename),
376
+ value: expr.value,
377
+ caseInsensitive: expr.caseInsensitive
355
378
  }
356
- }]
379
+ } else if t == "stringRegex" {
380
+ return { type: t, input: _renameExpression(expr.input, rename), value: expr.value }
381
+ } else if t == "stringContainsFuzzy" {
382
+ result := {
383
+ type: t,
384
+ input: _renameExpression(expr.input, rename),
385
+ value: expr.value,
386
+ maxEdits: expr.maxEdits,
387
+ caseInsensitive: expr.caseInsensitive,
388
+ substitutionsOnly: expr.substitutionsOnly
389
+ }
390
+ if !is_undefined(expr.wildcard) { result.wildcard = expr.wildcard }
391
+ return result
392
+ } else if t == "isNull" || t == "not" {
393
+ return { type: t, input: _renameExpression(expr.input, rename) }
394
+ } else if t == "ifNull" {
395
+ return {
396
+ type: "ifNull",
397
+ input: _renameExpression(expr.input, rename),
398
+ replacement: _renameExpression(expr.replacement, rename)
399
+ }
400
+ } else if t == "and" || t == "or" {
401
+ return { type: t, input: _renameExpressions(expr.input, rename) }
402
+ } else if t == "isIn" {
403
+ result := {
404
+ type: "isIn",
405
+ input: _renameExpression(expr.input, rename),
406
+ set: expr.set
407
+ }
408
+ if !is_undefined(expr.negate) { result.negate = expr.negate }
409
+ return result
410
+ }
411
+ ll.panic("renameSpecQueryColumns: unsupported expression variant %q", t)
412
+ }
357
413
 
358
- _FILTER_SCHEMA := {
414
+ _renameSpecQuery = func(query, rename) {
415
+ t := query.type
416
+ if t == "column" {
417
+ return { type: "column", column: rename[query.column] }
418
+ } else if t == "sliceAxes" {
419
+ return {
420
+ type: "sliceAxes",
421
+ input: _renameSpecQuery(query.input, rename),
422
+ axisFilters: query.axisFilters
423
+ }
424
+ } else if t == "innerJoin" || t == "fullJoin" {
425
+ return { type: t, entries: _renameJoinEntries(query.entries, rename) }
426
+ } else if t == "outerJoin" {
427
+ return {
428
+ type: "outerJoin",
429
+ primary: _renameJoinEntry(query.primary, rename),
430
+ secondary: _renameJoinEntries(query.secondary, rename)
431
+ }
432
+ } else if t == "linkerJoin" {
433
+ return {
434
+ type: "linkerJoin",
435
+ linker: { column: rename[query.linker.column] },
436
+ secondary: _renameJoinEntries(query.secondary, rename)
437
+ }
438
+ } else if t == "filter" {
439
+ return {
440
+ type: "filter",
441
+ input: _renameSpecQuery(query.input, rename),
442
+ predicate: _renameExpression(query.predicate, rename)
443
+ }
444
+ }
445
+ ll.panic("renameSpecQueryColumns: unsupported SpecQuery variant %q", t)
446
+ }
359
447
 
360
- `column`: _P_TABLE_COLUMN_ID_SCHEMA,
361
448
 
362
- `predicate`: _PREDICATE_SCHEMA
363
- }
364
449
 
365
450
 
366
- _JOIN_REQUEST_SCHEMA := {
367
- `__options__,closed`: true,
368
451
 
369
- `src`: _JOIN_ENTRY_SCHEMA,
370
452
 
371
- `filters`: [_FILTER_SCHEMA]
372
- }
373
453
 
374
- _renameJoinEntry := func(entry, rename) {
375
- if entry.type == "column" || entry.type == "slicedColumn" || entry.type == "artificialColumn" {
376
- entry.columnId = rename[entry.columnId]
377
- } else if entry.type == "inner" || entry.type == "full" {
378
- entry.entries = slices.map(entry.entries, func(entry) {
379
- return _renameJoinEntry(entry, rename)
380
- })
381
- } else if entry.type == "outer" {
382
- entry.primary = _renameJoinEntry(entry.primary, rename)
383
- entry.secondary = slices.map(entry.secondary, func(entry) {
384
- return _renameJoinEntry(entry, rename)
385
- })
386
- }
387
- return entry
388
- }
389
454
 
390
- _renameJoinFilter := func(filter, rename) {
391
- if filter.column.type == "column" {
392
- filter.column.id = rename[filter.column.id]
393
- }
394
- return filter
395
- }
396
455
 
397
- renameJoinColumns := func(request, rename) {
398
- validation.assertType(request, _JOIN_REQUEST_SCHEMA)
456
+ renameSpecQueryColumns := func(query, rename) {
457
+ validation.assertType(query, _SPEC_QUERY_SCHEMA)
399
458
  validation.assertType(rename, {any: `string`})
400
- return {
401
- src: _renameJoinEntry(request.src, rename),
402
- filters: slices.map(request.filters, func(filter) {
403
- return _renameJoinFilter(filter, rename)
404
- })
405
- }
459
+ return _renameSpecQuery(query, rename)
406
460
  }
407
461
 
408
462
  export ll.toStrict({
@@ -411,6 +465,6 @@ export ll.toStrict({
411
465
  makeFrameColumnInfo: makeFrameColumnInfo,
412
466
  makeWriteFrameStep: makeWriteFrameStep,
413
467
  makeFrameColumnRef: makeFrameColumnRef,
414
- renameJoinColumns: renameJoinColumns,
468
+ renameSpecQueryColumns: renameSpecQueryColumns,
415
469
  validateAxisFilters: validateAxisFilters
416
470
  })
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/workflow-tengo",
3
- "version": "5.15.1",
3
+ "version": "5.17.0",
4
4
  "description": "Tengo SDK library",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -18,11 +18,11 @@
18
18
  "dependencies": {
19
19
  "@milaboratories/software-pframes-conv": "2.2.9",
20
20
  "@platforma-open/milaboratories.software-small-binaries": "^2.0.3",
21
- "@platforma-open/milaboratories.software-ptabler": "1.15.2",
21
+ "@platforma-open/milaboratories.software-ptabler": "1.16.0",
22
22
  "@platforma-open/milaboratories.software-ptexter": "1.2.2"
23
23
  },
24
24
  "devDependencies": {
25
- "@platforma-sdk/tengo-builder": "2.5.15"
25
+ "@platforma-sdk/tengo-builder": "2.5.18"
26
26
  },
27
27
  "scripts": {
28
28
  "type-check": "pl-tengo check",