pg_sql_ut.cpp 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  1. #include "ut/util.h"
  2. #include <library/cpp/testing/unittest/registar.h>
  3. #include <yql/essentials/parser/pg_wrapper/interface/parser.h>
  4. #include <util/system/tempfile.h>
  5. using namespace NSQLTranslation;
  6. Y_UNIT_TEST_SUITE(PgSqlParsingOnly) {
  7. Y_UNIT_TEST(Locking) {
  8. auto res = PgSqlToYql("SELECT 1 FROM plato.Input FOR UPDATE");
  9. UNIT_ASSERT(res.Root);
  10. UNIT_ASSERT_EQUAL(res.Issues.Size(), 1);
  11. auto issue = *(res.Issues.begin());
  12. UNIT_ASSERT(issue.GetMessage().find("locking") != TString::npos);
  13. }
  14. Y_UNIT_TEST(InsertStmt) {
  15. auto res = PgSqlToYql("INSERT INTO plato.Input VALUES (1, 1)");
  16. UNIT_ASSERT(res.Root);
  17. }
  18. Y_UNIT_TEST(InsertStmt_DefaultValues) {
  19. auto res = PgSqlToYql("INSERT INTO plato.Input DEFAULT VALUES");
  20. UNIT_ASSERT(res.Root);
  21. const NYql::TAstNode* writeNode = nullptr;
  22. VisitAstNodes(*res.Root, [&writeNode] (const NYql::TAstNode& node) {
  23. const bool isWriteNode = node.IsList() && node.GetChildrenCount() > 0
  24. && node.GetChild(0)->IsAtom() && node.GetChild(0)->GetContent() == "Write!";
  25. if (isWriteNode) {
  26. writeNode = &node;
  27. }
  28. });
  29. UNIT_ASSERT(writeNode);
  30. UNIT_ASSERT(writeNode->GetChildrenCount() > 5);
  31. const auto optionsQListNode = writeNode->GetChild(5);
  32. UNIT_ASSERT(optionsQListNode->ToString().Contains("'default_values"));
  33. }
  34. Y_UNIT_TEST(InsertStmt_Returning) {
  35. auto res = PgSqlToYql("INSERT INTO plato.Input VALUES (1, 1) RETURNING *");
  36. UNIT_ASSERT(res.Root);
  37. const NYql::TAstNode* writeNode = nullptr;
  38. VisitAstNodes(*res.Root, [&writeNode] (const NYql::TAstNode& node) {
  39. const bool isWriteNode = node.IsList() && node.GetChildrenCount() > 0
  40. && node.GetChild(0)->IsAtom() && node.GetChild(0)->GetContent() == "Write!";
  41. if (isWriteNode) {
  42. writeNode = &node;
  43. }
  44. });
  45. UNIT_ASSERT(writeNode);
  46. UNIT_ASSERT(writeNode->GetChildrenCount() > 5);
  47. const auto optionsQListNode = writeNode->GetChild(5);
  48. UNIT_ASSERT_STRINGS_EQUAL(
  49. optionsQListNode->ToString(),
  50. R"('('('mode 'append) '('returning '((PgResultItem '"" (Void) (lambda '() (PgStar)))))))"
  51. );
  52. }
  53. Y_UNIT_TEST(DeleteStmt) {
  54. auto res = PgSqlToYql("DELETE FROM plato.Input");
  55. UNIT_ASSERT(res.Root);
  56. TString program = R"(
  57. (
  58. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  59. (let read0 (Read! world (DataSource '"yt" '"plato") (Key '('table (String '"input"))) (Void) '()))
  60. (let world (Left! read0))
  61. (let world (Write! world (DataSink '"yt" '"plato") (Key '('table (String '"input"))) (Void) '('('pg_delete (PgSelect '('('set_items '((PgSetItem '('('result '((PgResultItem '"" (Void) (lambda '() (PgStar))))) '('from '('((Right! read0) '"input" '()))) '('join_ops '('('('push)))))))) '('set_ops '('push))))) '('mode 'delete))))
  62. (let world (CommitAll! world))
  63. (return world)
  64. )
  65. )";
  66. const auto expectedAst = NYql::ParseAst(program);
  67. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  68. }
  69. Y_UNIT_TEST(DeleteStmt_Returning) {
  70. auto res = PgSqlToYql("DELETE FROM plato.Input RETURNING name, price AS new_price");
  71. UNIT_ASSERT(res.Root);
  72. const NYql::TAstNode* writeNode = nullptr;
  73. VisitAstNodes(*res.Root, [&writeNode] (const NYql::TAstNode& node) {
  74. const bool isWriteNode = node.IsList() && node.GetChildrenCount() > 0
  75. && node.GetChild(0)->IsAtom() && node.GetChild(0)->GetContent() == "Write!";
  76. if (isWriteNode) {
  77. writeNode = &node;
  78. }
  79. });
  80. UNIT_ASSERT(writeNode);
  81. UNIT_ASSERT(writeNode->GetChildrenCount() > 5);
  82. const auto optionsQListNode = writeNode->GetChild(5);
  83. UNIT_ASSERT_STRINGS_EQUAL(
  84. optionsQListNode->GetChild(1)->GetChild(2)->ToString(),
  85. R"('('returning '((PgResultItem '"name" (Void) (lambda '() (PgColumnRef '"name"))) (PgResultItem '"new_price" (Void) (lambda '() (PgColumnRef '"price"))))))"
  86. );
  87. }
  88. Y_UNIT_TEST(CreateTableStmt_Basic) {
  89. auto res = PgSqlToYql("CREATE TABLE t (a int, b text)");
  90. UNIT_ASSERT(res.Root);
  91. TString program = R"(
  92. (
  93. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  94. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create) '('columns '('('a (PgType 'int4) '('columnConstraints '())) '('b (PgType 'text) '('columnConstraints '())))))))
  95. (let world (CommitAll! world))
  96. (return world)
  97. )
  98. )";
  99. const auto expectedAst = NYql::ParseAst(program);
  100. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  101. }
  102. Y_UNIT_TEST(CreateTableStmt_SystemColumns) {
  103. auto res = PgSqlToYql("CREATE TABLE t(XMIN int)");
  104. UNIT_ASSERT(!res.Root);
  105. UNIT_ASSERT_EQUAL(res.Issues.Size(), 1);
  106. auto issue = *(res.Issues.begin());
  107. UNIT_ASSERT(issue.GetMessage().find("system column") != TString::npos);
  108. }
  109. Y_UNIT_TEST(CreateTableStmt_NotNull) {
  110. auto res = PgSqlToYql("CREATE TABLE t (a int NOT NULL, b text)");
  111. UNIT_ASSERT(res.Root);
  112. TString program = R"(
  113. (
  114. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  115. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create) '('columns '('('a (PgType 'int4) '('columnConstraints '('('not_null)))) '('b (PgType 'text) '('columnConstraints '())))))))
  116. (let world (CommitAll! world))
  117. (return world)
  118. )
  119. )";
  120. const auto expectedAst = NYql::ParseAst(program);
  121. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  122. }
  123. Y_UNIT_TEST(CreateTableStmt_JustPK) {
  124. auto res = PgSqlToYql("CREATE TABLE t (a int PRIMARY KEY, b text)");
  125. UNIT_ASSERT(res.Root);
  126. TString program = R"(
  127. (
  128. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  129. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create) '('columns '('('a (PgType 'int4) '('columnConstraints '('('not_null)))) '('b (PgType 'text) '('columnConstraints '())))) '('primarykey '('a)))))
  130. (let world (CommitAll! world))
  131. (return world)
  132. )
  133. )";
  134. const auto expectedAst = NYql::ParseAst(program);
  135. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  136. }
  137. Y_UNIT_TEST(CreateTableStmt_Default) {
  138. auto res = PgSqlToYql("CREATE TABLE t (a int PRIMARY KEY, b int DEFAULT 0)");
  139. UNIT_ASSERT(res.Root);
  140. TString program = R"(
  141. (
  142. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  143. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create) '('columns '('('a (PgType 'int4) '('columnConstraints '('('not_null)))) '('b (PgType 'int4) '('columnConstraints '('('default (PgConst '0 (PgType 'int4)))))))) '('primarykey '('a)))))
  144. (let world (CommitAll! world))
  145. (return world)
  146. )
  147. )";
  148. const auto expectedAst = NYql::ParseAst(program);
  149. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  150. }
  151. Y_UNIT_TEST(CreateTableStmt_PKAndNotNull) {
  152. auto res = PgSqlToYql("CREATE TABLE t (a int PRIMARY KEY NOT NULL, b text)");
  153. UNIT_ASSERT(res.Root);
  154. TString program = R"(
  155. (
  156. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  157. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create) '('columns '('('a (PgType 'int4) '('columnConstraints '('('not_null)))) '('b (PgType 'text) '('columnConstraints '())))) '('primarykey '('a)))))
  158. (let world (CommitAll! world))
  159. (return world)
  160. )
  161. )";
  162. const auto expectedAst = NYql::ParseAst(program);
  163. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  164. }
  165. Y_UNIT_TEST(CreateTableStmt_PKAndOtherNotNull) {
  166. auto res = PgSqlToYql("CREATE TABLE t (a int PRIMARY KEY, b text NOT NULL)");
  167. UNIT_ASSERT(res.Root);
  168. TString program = R"(
  169. (
  170. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  171. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create) '('columns '('('a (PgType 'int4) '('columnConstraints '('('not_null)))) '('b (PgType 'text) '('columnConstraints '('('not_null)))))) '('primarykey '('a)))))
  172. (let world (CommitAll! world))
  173. (return world)
  174. )
  175. )";
  176. const auto expectedAst = NYql::ParseAst(program);
  177. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  178. }
  179. Y_UNIT_TEST(CreateTableStmt_TableLevelPK) {
  180. auto res = PgSqlToYql("CREATE TABLE t (a int, b text NOT NULL, PRIMARY KEY (a, b))");
  181. UNIT_ASSERT(res.Root);
  182. TString program = R"(
  183. (
  184. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  185. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create) '('columns '('('a (PgType 'int4) '('columnConstraints '('('not_null)))) '('b (PgType 'text) '('columnConstraints '('('not_null)))))) '('primarykey '('a 'b)))))
  186. (let world (CommitAll! world))
  187. (return world)
  188. )
  189. )";
  190. const auto expectedAst = NYql::ParseAst(program);
  191. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  192. }
  193. Y_UNIT_TEST(CreateTableStmt_RepeatingColumnNames) {
  194. auto res = PgSqlToYql("CREATE TABLE t (a int, a text)");
  195. UNIT_ASSERT(!res.Root);
  196. UNIT_ASSERT_EQUAL(res.Issues.Size(), 1);
  197. auto issue = *(res.Issues.begin());
  198. UNIT_ASSERT(issue.GetMessage().find("duplicate") != TString::npos);
  199. }
  200. Y_UNIT_TEST(CreateTableStmt_PKHasColumnsNotBelongingToTable_Fails) {
  201. auto res = PgSqlToYql("CREATE TABLE t (a int, primary key(b))");
  202. UNIT_ASSERT(!res.Root);
  203. UNIT_ASSERT_EQUAL(res.Issues.Size(), 1);
  204. auto issue = *(res.Issues.begin());
  205. UNIT_ASSERT(issue.GetMessage().find("PK column does not belong to table") != TString::npos);
  206. }
  207. Y_UNIT_TEST(CreateTableStmt_AliasSerialToIntType) {
  208. auto res = PgSqlToYql("CREATE TABLE t (a SerIAL)");
  209. UNIT_ASSERT(res.Root);
  210. TString program = R"(
  211. (
  212. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  213. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create) '('columns '('('a (PgType 'int4) '('columnConstraints '('('serial)))))))))
  214. (let world (CommitAll! world))
  215. (return world))
  216. )";
  217. const auto expectedAst = NYql::ParseAst(program);
  218. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  219. }
  220. Y_UNIT_TEST(CreateTableStmt_Temp) {
  221. auto res = PgSqlToYql("create temp table t ()");
  222. UNIT_ASSERT(res.Root);
  223. TString program = R"(
  224. (
  225. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  226. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create) '('columns '()) '('temporary))))
  227. (let world (CommitAll! world))
  228. (return world)
  229. )
  230. )";
  231. const auto expectedAst = NYql::ParseAst(program);
  232. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  233. }
  234. Y_UNIT_TEST(CreateSeqStmt) {
  235. auto res = PgSqlToYql(
  236. "CREATE TEMP SEQUENCE IF NOT EXISTS seq AS integer START WITH 10 INCREMENT BY 2 NO MINVALUE NO MAXVALUE CACHE 3;");
  237. UNIT_ASSERT_C(res.Root, res.Issues.ToString());
  238. TString program = R"(
  239. ((let world (Configure! world (DataSource 'config) 'OrderedColumns))
  240. (let world (Write! world (DataSink '"kikimr" '"")
  241. (Key '('pgObject (String '"seq") (String 'pgSequence))) (Void) '(
  242. '('mode 'create_if_not_exists) '('temporary) '('"as" '"int4")
  243. '('"start" '10) '('"increment" '2) '('"cache" '3))))
  244. (let world (CommitAll! world)) (return world))
  245. )";
  246. const auto expectedAst = NYql::ParseAst(program);
  247. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  248. }
  249. Y_UNIT_TEST(DropSequenceStmt) {
  250. auto res = PgSqlToYql("DROP SEQUENCE IF EXISTS seq;");
  251. UNIT_ASSERT_C(res.Root, res.Issues.ToString());
  252. TString program = R"(
  253. (
  254. (let world (Configure! world (DataSource 'config) 'OrderedColumns)) (let world (Write! world (DataSink '"kikimr" '"") (Key '('pgObject (String '"seq") (String 'pgSequence))) (Void) '('('mode 'drop_if_exists))))
  255. (let world (CommitAll! world))
  256. (return world)
  257. )
  258. )";
  259. const auto expectedAst = NYql::ParseAst(program);
  260. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  261. }
  262. Y_UNIT_TEST(AlterSequenceStmt) {
  263. auto res = PgSqlToYql("ALTER SEQUENCE IF EXISTS seq AS integer START WITH 10 RESTART WITH 101 INCREMENT BY 2 NO MINVALUE NO MAXVALUE CACHE 3;");
  264. UNIT_ASSERT_C(res.Root, res.Issues.ToString());
  265. TString program = R"(
  266. (
  267. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  268. (let world (Write! world (DataSink '"kikimr" '"")
  269. (Key '('pgObject (String '"seq") (String 'pgSequence)))
  270. (Void) '('('mode 'alter_if_exists) '('"as" '"int4") '('"start" '10) '('"restart" '101) '('"increment" '2) '('"cache" '3))))
  271. (let world (CommitAll! world)) (return world)
  272. )
  273. )";
  274. const auto expectedAst = NYql::ParseAst(program);
  275. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  276. }
  277. Y_UNIT_TEST(AlterTableSetDefaultNextvalStmt) {
  278. auto res = PgSqlToYql("ALTER TABLE public.t ALTER COLUMN id SET DEFAULT nextval('seq');");
  279. UNIT_ASSERT_C(res.Root, res.Issues.ToString());
  280. TString program = R"(
  281. (
  282. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  283. (let world (Write! world (DataSink '"kikimr" '"")
  284. (Key '('tablescheme (String '"t"))) (Void) '('('mode 'alter) '('actions '('('alterColumns '('('"id" '('setDefault '('nextval 'seq))))))))))
  285. (let world (CommitAll! world)) (return world)
  286. )
  287. )";
  288. const auto expectedAst = NYql::ParseAst(program);
  289. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  290. }
  291. Y_UNIT_TEST(AlterTableSetDefaultNextvalStmtWithSchemaname) {
  292. auto res = PgSqlToYql("ALTER TABLE public.t ALTER COLUMN id SET DEFAULT nextval('public.seq'::regclass);");
  293. UNIT_ASSERT_C(res.Root, res.Issues.ToString());
  294. TString program = R"(
  295. (
  296. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  297. (let world (Write! world (DataSink '"kikimr" '"")
  298. (Key '('tablescheme (String '"t"))) (Void) '('('mode 'alter) '('actions '('('alterColumns '('('"id" '('setDefault '('nextval 'seq))))))))))
  299. (let world (CommitAll! world)) (return world)
  300. )
  301. )";
  302. const auto expectedAst = NYql::ParseAst(program);
  303. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  304. }
  305. Y_UNIT_TEST(AlterTableStmtWithCast) {
  306. auto res = PgSqlToYql("ALTER TABLE public.t ALTER COLUMN id SET DEFAULT nextval('seq'::regclass);");
  307. UNIT_ASSERT_C(res.Root, res.Issues.ToString());
  308. TString program = R"(
  309. (
  310. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  311. (let world (Write! world (DataSink '"kikimr" '"")
  312. (Key '('tablescheme (String '"t"))) (Void) '('('mode 'alter) '('actions '('('alterColumns '('('"id" '('setDefault '('nextval 'seq))))))))))
  313. (let world (CommitAll! world)) (return world)
  314. )
  315. )";
  316. const auto expectedAst = NYql::ParseAst(program);
  317. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  318. }
  319. Y_UNIT_TEST(AlterTableDropDefaultStmt) {
  320. auto res = PgSqlToYql("ALTER TABLE public.t ALTER COLUMN id DROP DEFAULT;");
  321. UNIT_ASSERT_C(res.Root, res.Issues.ToString());
  322. TString program = R"(
  323. (
  324. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  325. (let world (Write! world (DataSink '"kikimr" '"")
  326. (Key '('tablescheme (String '"t"))) (Void) '('('mode 'alter) '('actions '('('alterColumns '('('"id" '('setDefault '('Null))))))))))
  327. (let world (CommitAll! world)) (return world)
  328. )
  329. )";
  330. const auto expectedAst = NYql::ParseAst(program);
  331. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  332. }
  333. Y_UNIT_TEST(VariableShowStmt) {
  334. auto res = PgSqlToYql("Show server_version_num");
  335. UNIT_ASSERT(res.Root);
  336. TString program = fmt::format(R"(
  337. (
  338. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  339. (let output (PgSelect '('('set_items '((PgSetItem '('('result '((PgResultItem '"server_version_num" (Void) (lambda '() (PgConst '"{}" (PgType 'text)))))))))) '('set_ops '('push)))))
  340. (let result_sink (DataSink 'result))
  341. (let world (Write! world result_sink (Key) output '('('type) '('autoref))))
  342. (let world (Commit! world result_sink))
  343. (let world (CommitAll! world))
  344. (return world)
  345. )
  346. )", NYql::GetPostgresServerVersionNum());
  347. const auto expectedAst = NYql::ParseAst(program);
  348. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  349. }
  350. TMap<TString, TString> GetParamNameToPgType(const NYql::TAstNode& root) {
  351. TMap<TString, TString> actualParamToType;
  352. VisitAstNodes(root, [&actualParamToType] (const NYql::TAstNode& node) {
  353. bool isDeclareNode =
  354. node.IsListOfSize(3) && node.GetChild(0)->IsAtom()
  355. && node.GetChild(0)->GetContent() == "declare";
  356. if (isDeclareNode) {
  357. const auto varNameNode = node.GetChild(1);
  358. UNIT_ASSERT(varNameNode->IsAtom());
  359. const auto varName = varNameNode->GetContent();
  360. const auto varTypeNode = node.GetChild(2);
  361. UNIT_ASSERT(varTypeNode->IsListOfSize(2));
  362. UNIT_ASSERT(varTypeNode->GetChild(0)->GetContent() == "PgType");
  363. actualParamToType[TString(varName)] = varTypeNode->GetChild(1)->ToString();
  364. }
  365. });
  366. return actualParamToType;
  367. }
  368. Y_UNIT_TEST(ParamRef_IntAndPoint) {
  369. TTranslationSettings settings;
  370. settings.PgParameterTypeOids = {NYql::NPg::LookupType("int4").TypeId, NYql::NPg::LookupType("point").TypeId};
  371. auto res = SqlToYqlWithMode(
  372. R"(select $1 as "x", $2 as "y")",
  373. NSQLTranslation::ESqlMode::QUERY,
  374. 10,
  375. {},
  376. EDebugOutput::None,
  377. false,
  378. settings);
  379. TMap<TString, TString> expectedParamToType {
  380. {"$p1", "'int4"},
  381. {"$p2", "'point"},
  382. };
  383. UNIT_ASSERT(res.Root);
  384. const auto actualParamToTypes = GetParamNameToPgType(*res.Root);
  385. UNIT_ASSERT(expectedParamToType.size() == actualParamToTypes.size());
  386. UNIT_ASSERT_EQUAL(expectedParamToType, actualParamToTypes);
  387. }
  388. Y_UNIT_TEST(ParamRef_IntUnknownInt) {
  389. TTranslationSettings settings;
  390. settings.PgParameterTypeOids = {NYql::NPg::LookupType("int4").TypeId, NYql::NPg::LookupType("unknown").TypeId, NYql::NPg::LookupType("int4").TypeId};
  391. auto res = SqlToYqlWithMode(
  392. R"(select $1 as "x", $2 as "y", $3 as "z")",
  393. NSQLTranslation::ESqlMode::QUERY,
  394. 10,
  395. {},
  396. EDebugOutput::None,
  397. false,
  398. settings);
  399. TMap<TString, TString> expectedParamToType {
  400. {"$p1", "'int4"},
  401. {"$p2", "'unknown"},
  402. {"$p3", "'int4"},
  403. };
  404. UNIT_ASSERT(res.Root);
  405. const auto actualParamToTypes = GetParamNameToPgType(*res.Root);
  406. UNIT_ASSERT(expectedParamToType.size() == actualParamToTypes.size());
  407. UNIT_ASSERT_EQUAL(expectedParamToType, actualParamToTypes);
  408. }
  409. Y_UNIT_TEST(ParamRef_NoTypeOids) {
  410. TTranslationSettings settings;
  411. settings.PgParameterTypeOids = {};
  412. auto res = PgSqlToYql(R"(select $1 as "x", $2 as "y", $3 as "z")");
  413. TMap<TString, TString> expectedParamToType {
  414. {"$p1", "'unknown"},
  415. {"$p2", "'unknown"},
  416. {"$p3", "'unknown"},
  417. };
  418. UNIT_ASSERT(res.Root);
  419. auto actualParamToTypes = GetParamNameToPgType(*res.Root);
  420. UNIT_ASSERT_VALUES_EQUAL(expectedParamToType, actualParamToTypes);
  421. }
  422. Y_UNIT_TEST(DropTableStmt) {
  423. auto res = PgSqlToYql("drop table plato.Input");
  424. TString program = R"(
  425. (
  426. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  427. (let world (Write! world (DataSink '"yt" '"plato") (Key '('tablescheme (String '"input"))) (Void) '('('mode 'drop))))
  428. (let world (CommitAll! world))
  429. (return world)
  430. )
  431. )";
  432. const auto expectedAst = NYql::ParseAst(program);
  433. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  434. }
  435. Y_UNIT_TEST(DropTableStmtMultiple) {
  436. auto res = PgSqlToYql("DROP TABLE FakeTable1, FakeTable2");
  437. TString program = R"(
  438. (
  439. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  440. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"FakeTable1"))) (Void) '('('mode 'drop))))
  441. (let world (Write! world (DataSink '"kikimr" '"") (Key '('tablescheme (String '"FakeTable2"))) (Void) '('('mode 'drop))))
  442. (let world (CommitAll! world))
  443. (return world)
  444. )
  445. )";
  446. const auto expectedAst = NYql::ParseAst(program);
  447. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  448. }
  449. Y_UNIT_TEST(DropTableUnknownClusterStmt) {
  450. auto res = PgSqlToYql("drop table if exists pub.t");
  451. UNIT_ASSERT(!res.IsOk());
  452. UNIT_ASSERT_EQUAL(res.Issues.Size(), 1);
  453. auto issue = *(res.Issues.begin());
  454. UNIT_ASSERT_C(issue.GetMessage().find("Unknown cluster: pub") != TString::npos, res.Issues.ToString());
  455. }
  456. Y_UNIT_TEST(PublicSchemeRemove) {
  457. auto res = PgSqlToYql("DROP TABLE IF EXISTS public.t; CREATE TABLE public.t(id INT PRIMARY KEY, foo INT);\
  458. INSERT INTO public.t VALUES(1, 2);\
  459. UPDATE public.t SET foo = 3 WHERE id == 1;\
  460. DELETE FROM public.t WHERE id == 1;\
  461. SELECT COUNT(*) FROM public.t;");
  462. UNIT_ASSERT(res.IsOk());
  463. UNIT_ASSERT(res.Root->ToString().find("public") == TString::npos);
  464. }
  465. Y_UNIT_TEST(UpdateStmt) {
  466. auto res = PgSqlToYql("UPDATE plato.Input SET kind = 'test' where kind = 'testtest'");
  467. TString updateStmtProg = R"(
  468. (
  469. (let world (Configure! world (DataSource 'config) 'OrderedColumns))
  470. (let read0 (Read! world (DataSource '"yt" '"plato") (Key '('table (String '"input"))) (Void) '()))
  471. (let world (Left! read0))
  472. (let world (block '((let update_select
  473. (PgSelect '(
  474. '('set_items '((PgSetItem
  475. '('('emit_pg_star)
  476. '('result '((PgResultItem '"" (Void) (lambda '() (PgStar)))
  477. (PgResultItem '"kind" (Void) (lambda '() (PgConst '"test" (PgType 'unknown))))))
  478. '('from '('((Right! read0) '"input" '())))
  479. '('join_ops '('('('push))))
  480. '('where (PgWhere (Void) (lambda '() (PgOp '"=" (PgColumnRef '"kind") (PgConst '"testtest" (PgType 'unknown)))))) '('unknowns_allowed)))))
  481. '('set_ops '('push)))
  482. )
  483. )
  484. (let sink (DataSink '"yt" '"plato"))
  485. (let key (Key '('table (String '"input"))))
  486. (return (Write! world sink key (Void) '('('pg_update update_select) '('mode 'update)))))))
  487. (let world (CommitAll! world))
  488. (return world)
  489. )
  490. )";
  491. const auto expectedAst = NYql::ParseAst(updateStmtProg);
  492. UNIT_ASSERT_C(res.Issues.Empty(), "Failed to parse statement, issues: " + res.Issues.ToString());
  493. UNIT_ASSERT_C(res.Root, "Failed to parse statement, root is nullptr");
  494. UNIT_ASSERT_STRINGS_EQUAL(res.Root->ToString(), expectedAst.Root->ToString());
  495. }
  496. Y_UNIT_TEST(BlockEngine) {
  497. auto res = PgSqlToYql("set blockEngine='auto'; select 1;");
  498. UNIT_ASSERT(res.Root);
  499. UNIT_ASSERT_STRING_CONTAINS(res.Root->ToString(), "(let world (Configure! world (DataSource 'config) 'BlockEngine 'auto))");
  500. res = PgSqlToYql("set Blockengine='force'; select 1;");
  501. UNIT_ASSERT(res.Root);
  502. UNIT_ASSERT_STRING_CONTAINS(res.Root->ToString(), "(let world (Configure! world (DataSource 'config) 'BlockEngine 'force))");
  503. res = PgSqlToYql("set BlockEngine='disable'; select 1;");
  504. UNIT_ASSERT(res.Root);
  505. UNIT_ASSERT(!res.Root->ToString().Contains("BlockEngine"));
  506. res = PgSqlToYql("set BlockEngine='foo'; select 1;");
  507. UNIT_ASSERT(!res.Root);
  508. UNIT_ASSERT_EQUAL(res.Issues.Size(), 1);
  509. auto issue = *(res.Issues.begin());
  510. UNIT_ASSERT(issue.GetMessage().Contains("VariableSetStmt, not supported BlockEngine option value: foo"));
  511. }
  512. Y_UNIT_TEST(SetConfig_SearchPath) {
  513. TTranslationSettings settings;
  514. settings.GUCSettings = std::make_shared<TGUCSettings>();
  515. settings.ClusterMapping["pg_catalog"] = NYql::PgProviderName;
  516. settings.DefaultCluster = "";
  517. auto res = SqlToYqlWithMode(
  518. R"(select set_config('search_path', 'pg_catalog', false);)",
  519. NSQLTranslation::ESqlMode::QUERY,
  520. 10,
  521. {},
  522. EDebugOutput::ToCerr,
  523. false,
  524. settings);
  525. UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
  526. UNIT_ASSERT(res.Root);
  527. res = SqlToYqlWithMode(
  528. R"(select oid,
  529. typinput::int4 as typinput,
  530. typname,
  531. typnamespace,
  532. typtype
  533. from pg_type)",
  534. NSQLTranslation::ESqlMode::QUERY,
  535. 10,
  536. {},
  537. EDebugOutput::None,
  538. false,
  539. settings);
  540. UNIT_ASSERT(res.IsOk());
  541. UNIT_ASSERT(res.Root);
  542. res = SqlToYqlWithMode(
  543. R"(select oid,
  544. typinput::int4 as typinput,
  545. typname,
  546. typnamespace,
  547. typtype
  548. from pg_catalog.pg_type)",
  549. NSQLTranslation::ESqlMode::QUERY,
  550. 10,
  551. {},
  552. EDebugOutput::None,
  553. false,
  554. settings);
  555. UNIT_ASSERT(res.IsOk());
  556. UNIT_ASSERT(res.Root);
  557. res = SqlToYqlWithMode(
  558. R"(select set_config('search_path', 'public', false);)",
  559. NSQLTranslation::ESqlMode::QUERY,
  560. 10,
  561. {},
  562. EDebugOutput::None,
  563. false,
  564. settings);
  565. UNIT_ASSERT(res.IsOk());
  566. UNIT_ASSERT(res.Root);
  567. res = SqlToYqlWithMode(
  568. R"(select * from pg_type;)",
  569. NSQLTranslation::ESqlMode::QUERY,
  570. 10,
  571. {},
  572. EDebugOutput::None,
  573. false,
  574. settings);
  575. UNIT_ASSERT(res.IsOk());
  576. UNIT_ASSERT(res.Root);
  577. }
  578. }
  579. Y_UNIT_TEST_SUITE(PgExtensions) {
  580. using namespace NYql;
  581. Y_UNIT_TEST(Empty) {
  582. NPg::ClearExtensions();
  583. UNIT_ASSERT_VALUES_EQUAL(NPg::ExportExtensions(), "");
  584. NPg::ImportExtensions("", true, nullptr);
  585. }
  586. Y_UNIT_TEST(ProcsAndType) {
  587. NPg::ClearExtensions();
  588. if (NPg::AreAllFunctionsAllowed()) {
  589. return;
  590. }
  591. NPg::TExtensionDesc desc;
  592. TTempFileHandle h;
  593. TStringBuf sql = R"(
  594. CREATE OR REPLACE FUNCTION mytype_in(cstring)
  595. RETURNS mytype
  596. AS '$libdir/MyExt','mytype_in_func'
  597. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  598. CREATE OR REPLACE FUNCTION mytype_out(mytype)
  599. RETURNS cstring
  600. AS '$libdir/MyExt','mytype_out_func'
  601. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  602. CREATE TYPE mytype (
  603. alignment = double,
  604. internallength = 65,
  605. input = mytype_in,
  606. output = mytype_out
  607. );
  608. )";
  609. h.Write(sql.data(), sql.size());
  610. desc.Name = "MyExt";
  611. desc.InstallName = "$libdir/MyExt";
  612. desc.SqlPaths.push_back(h.Name());
  613. NPg::RegisterExtensions({desc}, true, *NSQLTranslationPG::CreateExtensionSqlParser(), nullptr);
  614. auto validate = [&]() {
  615. const auto& type = NPg::LookupType("mytype");
  616. UNIT_ASSERT_VALUES_EQUAL(type.Category, 'U');
  617. UNIT_ASSERT_VALUES_EQUAL(type.TypeLen, 65);
  618. UNIT_ASSERT_VALUES_EQUAL(type.TypeAlign, 'd');
  619. const auto& arrType = NPg::LookupType("_mytype");
  620. UNIT_ASSERT_VALUES_EQUAL(arrType.ElementTypeId, type.TypeId);
  621. const auto& inProc = NPg::LookupProc("mytype_in", { NPg::LookupType("cstring").TypeId });
  622. UNIT_ASSERT_VALUES_EQUAL(inProc.ArgTypes.size(), 1);
  623. UNIT_ASSERT_VALUES_EQUAL(inProc.Src, "mytype_in_func");
  624. UNIT_ASSERT(inProc.IsStrict);
  625. const auto& outProc = NPg::LookupProc("mytype_out", { NPg::LookupType("mytype").TypeId });
  626. UNIT_ASSERT_VALUES_EQUAL(outProc.ArgTypes.size(), 1);
  627. UNIT_ASSERT_VALUES_EQUAL(outProc.Src, "mytype_out_func");
  628. UNIT_ASSERT(outProc.IsStrict);
  629. UNIT_ASSERT_VALUES_EQUAL(type.InFuncId, inProc.ProcId);
  630. UNIT_ASSERT_VALUES_EQUAL(type.OutFuncId, outProc.ProcId);
  631. };
  632. validate();
  633. auto exported = NPg::ExportExtensions();
  634. NPg::ClearExtensions();
  635. NPg::ImportExtensions(exported, true, nullptr);
  636. validate();
  637. }
  638. Y_UNIT_TEST(InsertValues) {
  639. NPg::ClearExtensions();
  640. NPg::TExtensionDesc desc;
  641. TTempFileHandle h;
  642. TStringBuf sql = R"(
  643. CREATE TABLE mytable(
  644. foo int4,
  645. bar text,
  646. baz double
  647. );
  648. INSERT INTO mytable(bar, foo, baz)
  649. VALUES ('a', 1, null),('b', null, -3.4);
  650. )";
  651. h.Write(sql.data(), sql.size());
  652. desc.Name = "MyExt";
  653. desc.InstallName = "$libdir/MyExt";
  654. desc.SqlPaths.push_back(h.Name());
  655. NPg::RegisterExtensions({desc}, true, *NSQLTranslationPG::CreateExtensionSqlParser(), nullptr);
  656. auto validate = [&]() {
  657. const auto& table = NPg::LookupStaticTable({"pg_catalog","mytable"});
  658. UNIT_ASSERT(table.Kind == NPg::ERelKind::Relation);
  659. size_t remap[2];
  660. size_t rowStep;
  661. const auto& data = *NPg::ReadTable({"pg_catalog", "mytable"}, {"foo", "bar"}, remap, rowStep);
  662. UNIT_ASSERT_VALUES_EQUAL(rowStep, 3);
  663. UNIT_ASSERT_VALUES_EQUAL(data.size(), 2 * rowStep);
  664. UNIT_ASSERT_VALUES_EQUAL(data[rowStep * 0 + remap[0]], "1");
  665. UNIT_ASSERT_VALUES_EQUAL(data[rowStep * 0 + remap[1]], "a");
  666. UNIT_ASSERT(!data[rowStep * 1 + remap[0]].Defined());
  667. UNIT_ASSERT_VALUES_EQUAL(data[rowStep * 1 + remap[1]], "b");
  668. };
  669. validate();
  670. auto exported = NPg::ExportExtensions();
  671. NPg::ClearExtensions();
  672. NPg::ImportExtensions(exported, true, nullptr);
  673. validate();
  674. }
  675. Y_UNIT_TEST(Casts) {
  676. NPg::ClearExtensions();
  677. if (NPg::AreAllFunctionsAllowed()) {
  678. return;
  679. }
  680. NPg::TExtensionDesc desc;
  681. TTempFileHandle h;
  682. TStringBuf sql = R"(
  683. CREATE TYPE foo (
  684. alignment = double,
  685. internallength = variable
  686. );
  687. CREATE TYPE bar (
  688. alignment = double,
  689. internallength = variable
  690. );
  691. CREATE OR REPLACE FUNCTION bar(foo)
  692. RETURNS bar
  693. AS '$libdir/MyExt','foo_to_bar'
  694. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  695. CREATE CAST (foo AS bar) WITH FUNCTION bar(foo);
  696. )";
  697. h.Write(sql.data(), sql.size());
  698. desc.Name = "MyExt";
  699. desc.InstallName = "$libdir/MyExt";
  700. desc.SqlPaths.push_back(h.Name());
  701. NPg::RegisterExtensions({desc}, true, *NSQLTranslationPG::CreateExtensionSqlParser(), nullptr);
  702. auto validate = [&]() {
  703. auto sourceId = NPg::LookupType("foo").TypeId;
  704. auto targetId = NPg::LookupType("bar").TypeId;
  705. UNIT_ASSERT(NPg::HasCast(sourceId, targetId));
  706. const auto& cast = NPg::LookupCast(sourceId, targetId);
  707. UNIT_ASSERT_VALUES_EQUAL(cast.SourceId, sourceId);
  708. UNIT_ASSERT_VALUES_EQUAL(cast.TargetId, targetId);
  709. UNIT_ASSERT_VALUES_EQUAL((ui32)cast.Method, (ui32)NPg::ECastMethod::Function);
  710. UNIT_ASSERT_VALUES_EQUAL(cast.CoercionCode, NPg::ECoercionCode::Explicit);
  711. UNIT_ASSERT_VALUES_EQUAL(cast.FunctionId, NPg::LookupProc("bar",{sourceId}).ProcId);
  712. };
  713. validate();
  714. auto exported = NPg::ExportExtensions();
  715. NPg::ClearExtensions();
  716. NPg::ImportExtensions(exported, true, nullptr);
  717. validate();
  718. }
  719. Y_UNIT_TEST(Operators) {
  720. NPg::ClearExtensions();
  721. if (NPg::AreAllFunctionsAllowed()) {
  722. return;
  723. }
  724. NPg::TExtensionDesc desc;
  725. TTempFileHandle h;
  726. TStringBuf sql = R"(
  727. CREATE TYPE foo (
  728. alignment = double,
  729. internallength = variable
  730. );
  731. CREATE OR REPLACE FUNCTION foo_lt(foo, foo)
  732. RETURNS bool
  733. AS '$libdir/MyExt','foo_lt'
  734. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  735. CREATE OR REPLACE FUNCTION foo_le(foo, foo)
  736. RETURNS bool
  737. AS '$libdir/MyExt','foo_le'
  738. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  739. CREATE OR REPLACE FUNCTION foo_gt(foo, foo)
  740. RETURNS bool
  741. AS '$libdir/MyExt','foo_gt'
  742. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  743. CREATE OR REPLACE FUNCTION foo_ge(foo, foo)
  744. RETURNS bool
  745. AS '$libdir/MyExt','foo_ge'
  746. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  747. CREATE OPERATOR < (
  748. LEFTARG = foo, RIGHTARG = foo, PROCEDURE = foo_lt,
  749. COMMUTATOR = '>', NEGATOR = '>='
  750. );
  751. CREATE OPERATOR <= (
  752. LEFTARG = foo, RIGHTARG = foo, PROCEDURE = foo_le,
  753. COMMUTATOR = '>=', NEGATOR = '>'
  754. );
  755. CREATE OPERATOR > (
  756. LEFTARG = foo, RIGHTARG = foo, PROCEDURE = foo_gt,
  757. COMMUTATOR = '<', NEGATOR = '<='
  758. );
  759. CREATE OPERATOR >= (
  760. LEFTARG = foo, RIGHTARG = foo, PROCEDURE = foo_ge,
  761. COMMUTATOR = '<=', NEGATOR = '<'
  762. );
  763. )";
  764. h.Write(sql.data(), sql.size());
  765. desc.Name = "MyExt";
  766. desc.InstallName = "$libdir/MyExt";
  767. desc.SqlPaths.push_back(h.Name());
  768. NPg::RegisterExtensions({desc}, true, *NSQLTranslationPG::CreateExtensionSqlParser(), nullptr);
  769. auto validate = [&]() {
  770. auto typeId = NPg::LookupType("foo").TypeId;
  771. TVector<ui32> args { typeId, typeId };
  772. auto lessProcId = NPg::LookupProc("foo_lt", args).ProcId;
  773. auto lessOrEqualProcId = NPg::LookupProc("foo_le", args).ProcId;
  774. auto greaterProcId = NPg::LookupProc("foo_gt", args).ProcId;
  775. auto greaterOrEqualProcId = NPg::LookupProc("foo_ge", args).ProcId;
  776. const auto& lessOp = NPg::LookupOper("<", args);
  777. const auto& lessOrEqualOp = NPg::LookupOper("<=", args);
  778. const auto& greaterOp = NPg::LookupOper(">", args);
  779. const auto& greaterOrEqualOp = NPg::LookupOper(">=", args);
  780. UNIT_ASSERT_VALUES_EQUAL(lessOp.Name, "<");
  781. UNIT_ASSERT_VALUES_EQUAL(lessOp.LeftType, typeId);
  782. UNIT_ASSERT_VALUES_EQUAL(lessOp.RightType, typeId);
  783. UNIT_ASSERT_VALUES_EQUAL(lessOp.ProcId, lessProcId);
  784. UNIT_ASSERT_VALUES_EQUAL(lessOp.ComId, greaterOp.OperId);
  785. UNIT_ASSERT_VALUES_EQUAL(lessOp.NegateId, greaterOrEqualOp.OperId);
  786. UNIT_ASSERT_VALUES_EQUAL(lessOrEqualOp.Name, "<=");
  787. UNIT_ASSERT_VALUES_EQUAL(lessOrEqualOp.LeftType, typeId);
  788. UNIT_ASSERT_VALUES_EQUAL(lessOrEqualOp.RightType, typeId);
  789. UNIT_ASSERT_VALUES_EQUAL(lessOrEqualOp.ProcId, lessOrEqualProcId);
  790. UNIT_ASSERT_VALUES_EQUAL(lessOrEqualOp.ComId, greaterOrEqualOp.OperId);
  791. UNIT_ASSERT_VALUES_EQUAL(lessOrEqualOp.NegateId, greaterOp.OperId);
  792. UNIT_ASSERT_VALUES_EQUAL(greaterOp.Name, ">");
  793. UNIT_ASSERT_VALUES_EQUAL(greaterOp.LeftType, typeId);
  794. UNIT_ASSERT_VALUES_EQUAL(greaterOp.RightType, typeId);
  795. UNIT_ASSERT_VALUES_EQUAL(greaterOp.ProcId, greaterProcId);
  796. UNIT_ASSERT_VALUES_EQUAL(greaterOp.ComId, lessOp.OperId);
  797. UNIT_ASSERT_VALUES_EQUAL(greaterOp.NegateId, lessOrEqualOp.OperId);
  798. UNIT_ASSERT_VALUES_EQUAL(greaterOrEqualOp.Name, ">=");
  799. UNIT_ASSERT_VALUES_EQUAL(greaterOrEqualOp.LeftType, typeId);
  800. UNIT_ASSERT_VALUES_EQUAL(greaterOrEqualOp.RightType, typeId);
  801. UNIT_ASSERT_VALUES_EQUAL(greaterOrEqualOp.ProcId, greaterOrEqualProcId);
  802. UNIT_ASSERT_VALUES_EQUAL(greaterOrEqualOp.ComId, lessOrEqualOp.OperId);
  803. UNIT_ASSERT_VALUES_EQUAL(greaterOrEqualOp.NegateId, lessOp.OperId);
  804. };
  805. validate();
  806. auto exported = NPg::ExportExtensions();
  807. NPg::ClearExtensions();
  808. NPg::ImportExtensions(exported, true, nullptr);
  809. validate();
  810. }
  811. Y_UNIT_TEST(Aggregates) {
  812. NPg::ClearExtensions();
  813. if (NPg::AreAllFunctionsAllowed()) {
  814. return;
  815. }
  816. NPg::TExtensionDesc desc;
  817. TTempFileHandle h;
  818. TStringBuf sql = R"(
  819. CREATE TYPE foo (
  820. alignment = double,
  821. internallength = variable
  822. );
  823. CREATE OR REPLACE FUNCTION foo_agg_trans(internal, foo)
  824. RETURNS internal
  825. AS '$libdir/MyExt','foo_agg_trans'
  826. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  827. CREATE OR REPLACE FUNCTION foo_agg_final(internal)
  828. RETURNS foo
  829. AS '$libdir/MyExt','foo_agg_final'
  830. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  831. CREATE OR REPLACE FUNCTION foo_agg_combine(internal, internal)
  832. RETURNS internal
  833. AS '$libdir/MyExt','foo_agg_combine'
  834. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  835. CREATE OR REPLACE FUNCTION foo_agg_serial(internal)
  836. RETURNS bytea
  837. AS '$libdir/MyExt','foo_agg_serial'
  838. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  839. CREATE OR REPLACE FUNCTION foo_agg_deserial(bytea, internal)
  840. RETURNS internal
  841. AS '$libdir/MyExt','foo_agg_deserial'
  842. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  843. CREATE AGGREGATE foo_agg(foo) (
  844. sfunc = foo_agg_trans,
  845. stype = internal,
  846. serialfunc = foo_agg_serial,
  847. deserialfunc = foo_agg_deserial,
  848. combinefunc = foo_agg_combine,
  849. finalfunc = foo_agg_final
  850. );
  851. )";
  852. h.Write(sql.data(), sql.size());
  853. desc.Name = "MyExt";
  854. desc.InstallName = "$libdir/MyExt";
  855. desc.SqlPaths.push_back(h.Name());
  856. NPg::RegisterExtensions({desc}, true, *NSQLTranslationPG::CreateExtensionSqlParser(), nullptr);
  857. auto validate = [&]() {
  858. auto typeId = NPg::LookupType("foo").TypeId;
  859. auto internalTypeId = NPg::LookupType("internal").TypeId;
  860. auto byteaTypeId = NPg::LookupType("bytea").TypeId;
  861. const auto& desc = NPg::LookupAggregation("foo_agg", { typeId });
  862. UNIT_ASSERT_VALUES_EQUAL(desc.Name, "foo_agg");
  863. UNIT_ASSERT_VALUES_EQUAL(desc.ArgTypes, TVector<ui32>{typeId});
  864. UNIT_ASSERT_VALUES_EQUAL(desc.TransTypeId, internalTypeId);
  865. UNIT_ASSERT_VALUES_EQUAL(desc.TransFuncId, NPg::LookupProc("foo_agg_trans", {internalTypeId, typeId}).ProcId);
  866. UNIT_ASSERT_VALUES_EQUAL(desc.FinalFuncId, NPg::LookupProc("foo_agg_final", {internalTypeId}).ProcId);
  867. UNIT_ASSERT_VALUES_EQUAL(desc.SerializeFuncId, NPg::LookupProc("foo_agg_serial", {internalTypeId}).ProcId);
  868. UNIT_ASSERT_VALUES_EQUAL(desc.DeserializeFuncId, NPg::LookupProc("foo_agg_deserial", {byteaTypeId, internalTypeId}).ProcId);
  869. UNIT_ASSERT_VALUES_EQUAL(desc.CombineFuncId, NPg::LookupProc("foo_agg_combine", {internalTypeId, internalTypeId}).ProcId);
  870. };
  871. validate();
  872. auto exported = NPg::ExportExtensions();
  873. NPg::ClearExtensions();
  874. NPg::ImportExtensions(exported, true, nullptr);
  875. validate();
  876. }
  877. Y_UNIT_TEST(OpClasses) {
  878. NPg::ClearExtensions();
  879. if (NPg::AreAllFunctionsAllowed()) {
  880. return;
  881. }
  882. NPg::TExtensionDesc desc;
  883. TTempFileHandle h;
  884. TStringBuf sql = R"(
  885. CREATE TYPE foo (
  886. alignment = double,
  887. internallength = variable
  888. );
  889. CREATE OR REPLACE FUNCTION foo_lt(foo, foo)
  890. RETURNS bool
  891. AS '$libdir/MyExt','foo_lt'
  892. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  893. CREATE OR REPLACE FUNCTION foo_le(foo, foo)
  894. RETURNS bool
  895. AS '$libdir/MyExt','foo_le'
  896. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  897. CREATE OR REPLACE FUNCTION foo_gt(foo, foo)
  898. RETURNS bool
  899. AS '$libdir/MyExt','foo_gt'
  900. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  901. CREATE OR REPLACE FUNCTION foo_ge(foo, foo)
  902. RETURNS bool
  903. AS '$libdir/MyExt','foo_ge'
  904. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  905. CREATE OR REPLACE FUNCTION foo_eq(foo, foo)
  906. RETURNS bool
  907. AS '$libdir/MyExt','foo_eq'
  908. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  909. CREATE OR REPLACE FUNCTION foo_cmp(foo, foo)
  910. RETURNS interger
  911. AS '$libdir/MyExt','foo_cmp'
  912. LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
  913. CREATE OPERATOR < (
  914. LEFTARG = foo, RIGHTARG = foo, PROCEDURE = foo_lt,
  915. COMMUTATOR = '>', NEGATOR = '>='
  916. );
  917. CREATE OPERATOR <= (
  918. LEFTARG = foo, RIGHTARG = foo, PROCEDURE = foo_le,
  919. COMMUTATOR = '>=', NEGATOR = '>'
  920. );
  921. CREATE OPERATOR > (
  922. LEFTARG = foo, RIGHTARG = foo, PROCEDURE = foo_gt,
  923. COMMUTATOR = '<', NEGATOR = '<='
  924. );
  925. CREATE OPERATOR >= (
  926. LEFTARG = foo, RIGHTARG = foo, PROCEDURE = foo_ge,
  927. COMMUTATOR = '<=', NEGATOR = '<'
  928. );
  929. CREATE OPERATOR = (
  930. LEFTARG = foo, RIGHTARG = foo, PROCEDURE = foo_eq
  931. );
  932. CREATE OPERATOR CLASS btree_foo_ops
  933. DEFAULT FOR TYPE foo USING btree AS
  934. OPERATOR 1 < ,
  935. OPERATOR 2 <= ,
  936. OPERATOR 3 = ,
  937. OPERATOR 4 >= ,
  938. OPERATOR 5 > ,
  939. FUNCTION 1 foo_cmp (foo1 foo, foo2 foo);
  940. CREATE OR REPLACE FUNCTION foo_hash(foo)
  941. RETURNS integer
  942. AS '$libdir/MyExt','foo_hash'
  943. LANGUAGE 'c' STRICT IMMUTABLE PARALLEL SAFE;
  944. CREATE OPERATOR CLASS hash_foo_ops
  945. DEFAULT FOR TYPE foo USING hash AS
  946. OPERATOR 1 = ,
  947. FUNCTION 1 foo_hash(foo);
  948. )";
  949. h.Write(sql.data(), sql.size());
  950. desc.Name = "MyExt";
  951. desc.InstallName = "$libdir/MyExt";
  952. desc.SqlPaths.push_back(h.Name());
  953. NPg::RegisterExtensions({desc}, true, *NSQLTranslationPG::CreateExtensionSqlParser(), nullptr);
  954. auto validate = [&]() {
  955. const auto& typeDesc = NPg::LookupType("foo");
  956. auto typeId = typeDesc.TypeId;
  957. TVector<ui32> args { typeId, typeId };
  958. UNIT_ASSERT_VALUES_EQUAL(typeDesc.CompareProcId, NPg::LookupProc("foo_cmp", args).ProcId);
  959. UNIT_ASSERT_VALUES_EQUAL(typeDesc.LessProcId, NPg::LookupProc("foo_lt", args).ProcId);
  960. UNIT_ASSERT_VALUES_EQUAL(typeDesc.EqualProcId, NPg::LookupProc("foo_eq", args).ProcId);
  961. UNIT_ASSERT_VALUES_EQUAL(typeDesc.HashProcId, NPg::LookupProc("foo_hash", {typeId}).ProcId);
  962. const auto& opClassBtree = *NPg::LookupDefaultOpClass(NPg::EOpClassMethod::Btree, typeId);
  963. UNIT_ASSERT_VALUES_EQUAL(opClassBtree.Name, "btree_foo_ops");
  964. UNIT_ASSERT_VALUES_EQUAL(opClassBtree.Family, "btree/btree_foo_ops");
  965. UNIT_ASSERT_VALUES_EQUAL(NPg::LookupAmOp(opClassBtree.FamilyId, (ui32)NPg::EBtreeAmStrategy::Less, typeId, typeId).OperId,
  966. NPg::LookupOper("<", args).OperId);
  967. UNIT_ASSERT_VALUES_EQUAL(NPg::LookupAmProc(opClassBtree.FamilyId, (ui32)NPg::EBtreeAmProcNum::Compare, typeId, typeId).ProcId,
  968. NPg::LookupProc("foo_cmp", args).ProcId);
  969. const auto& opClassHash = *NPg::LookupDefaultOpClass(NPg::EOpClassMethod::Hash, typeId);
  970. UNIT_ASSERT_VALUES_EQUAL(opClassHash.Name, "hash_foo_ops");
  971. UNIT_ASSERT_VALUES_EQUAL(opClassHash.Family, "hash/hash_foo_ops");
  972. UNIT_ASSERT_VALUES_EQUAL(NPg::LookupAmProc(opClassHash.FamilyId, (ui32)NPg::EHashAmProcNum::Hash, typeId, typeId).ProcId,
  973. NPg::LookupProc("foo_hash", {typeId}).ProcId);
  974. };
  975. validate();
  976. auto exported = NPg::ExportExtensions();
  977. NPg::ClearExtensions();
  978. NPg::ImportExtensions(exported, true, nullptr);
  979. validate();
  980. }
  981. }