Re: Make COPY format extendable: Extract COPY TO format implementations - Mailing list pgsql-hackers
From | Sutou Kouhei |
---|---|
Subject | Re: Make COPY format extendable: Extract COPY TO format implementations |
Date | |
Msg-id | 20240730.161306.1949365750142390208.kou@clear-code.com Whole thread Raw |
In response to | Re: Make COPY format extendable: Extract COPY TO format implementations (Tomas Vondra <tomas.vondra@enterprisedb.com>) |
Responses |
Re: Make COPY format extendable: Extract COPY TO format implementations
|
List | pgsql-hackers |
Hi, In <26541788-8853-4d93-86cd-5f701b13ae51@enterprisedb.com> "Re: Make COPY format extendable: Extract COPY TO format implementations" on Mon, 29 Jul 2024 14:17:08 +0200, Tomas Vondra <tomas.vondra@enterprisedb.com> wrote: > I wrote a simple script to automate the benchmark - it just runs these > tests with different parameters (number of columns and number of > imported/exported rows). See the run.sh attachment, along with two CSV > results from current master and with all patches applied. Thanks. I also used the script with some modifications: 1. Create a test database automatically 2. Enable blackhole_am automatically 3. Create create_table_cols() automatically I attach it. I also attach results of master and patched. My results are from my desktop. So it's probably noisy. > - For COPY FROM there is no difference - the results are within 1% of > master, and there's no systemic difference. > > - For COPY TO it's a different story, though. There's a pretty clear > regression, by ~5%. It's a bit interesting the correlation with the > number of columns is not stronger ... My results showed different trend: - COPY FROM: Patched is about 15-20% slower than master - COPY TO: Patched is a bit faster than master Here are some my numbers: type n_cols n_rows diff master patched ---------------------------------------------------------- TO 5 1 100.56% 218.376000 219.609000 FROM 5 1 113.33% 168.493000 190.954000 ... TO 5 5 100.60% 1037.773000 1044.045000 FROM 5 5 116.46% 767.966000 894.377000 ... TO 5 10 100.15% 2092.245000 2095.472000 FROM 5 10 115.91% 1508.160000 1748.130000 TO 10 1 98.62% 353.087000 348.214000 FROM 10 1 118.65% 260.551000 309.133000 ... TO 10 5 96.89% 1724.061000 1670.427000 FROM 10 5 119.92% 1224.098000 1467.941000 ... TO 10 10 98.70% 3444.291000 3399.538000 FROM 10 10 118.79% 2462.314000 2924.866000 TO 15 1 97.71% 492.082000 480.802000 FROM 15 1 115.59% 347.820000 402.033000 ... TO 15 5 98.32% 2402.419000 2362.140000 FROM 15 5 115.48% 1657.594000 1914.245000 ... TO 15 10 96.91% 4830.319000 4681.145000 FROM 15 10 115.09% 3304.798000 3803.542000 TO 20 1 96.05% 629.828000 604.939000 FROM 20 1 118.50% 438.673000 519.839000 ... TO 20 5 97.15% 3084.210000 2996.331000 FROM 20 5 115.35% 2110.909000 2435.032000 ... TO 25 1 98.29% 764.779000 751.684000 FROM 25 1 115.13% 519.686000 598.301000 ... TO 25 5 94.08% 3843.996000 3616.614000 FROM 25 5 115.62% 2554.008000 2952.928000 ... TO 25 10 97.41% 7504.865000 7310.549000 FROM 25 10 117.25% 4994.463000 5856.029000 TO 30 1 94.39% 906.324000 855.503000 FROM 30 1 119.60% 604.110000 722.491000 ... TO 30 5 96.50% 4419.907000 4265.417000 FROM 30 5 116.97% 2932.883000 3430.556000 ... TO 30 10 94.39% 8974.878000 8470.991000 FROM 30 10 117.84% 5800.793000 6835.900000 ---- See the attached diff.txt for full numbers. I also attach scripts to generate the diff.txt. Here is the command line I used: ---- ruby diff.rb <(ruby aggregate.rb master.result) <(ruby aggregate.rb patched.result) | tee diff.txt ---- My environment: * Debian GNU/Linux sid * gcc (Debian 13.3.0-2) 13.3.0 * AMD Ryzen 9 3900X 12-Core Processor I'll look into this. If someone is interested in this proposal, could you share your numbers? > It's interesting the main change in the flamegraphs is CopyToStateFlush > pops up on the left side. Because, what is that about? That is a thing > introduced in the 0005 patch, so maybe the regression is not strictly > about the existing formats moving to the new API, but due to something > else in a later version of the patch? Ah, making static CopySendEndOfRow() a to non-static function (CopyToStateFlush()) may be the reason of this. Could you try the attached v19 patch? It changes the 0005 patch: * It reverts the static change * It adds a new non-static function that just exports CopySendEndOfRow() Thanks, -- kou #!/usr/bin/bash DIR=${1:-$(pwd)} psql postgres > /dev/null 2>&1 <<EOF DROP DATABASE IF EXISTS test; CREATE DATABASE test; EOF psql test > /dev/null 2>&1 <<EOF CREATE EXTENSION blackhole_am; CREATE OR REPLACE FUNCTION create_table_cols(tabname text, num_cols int) RETURNS VOID AS \$func\$ DECLARE query text; BEGIN query := 'CREATE UNLOGGED TABLE ' || tabname || ' ('; FOR i IN 1..num_cols LOOP query := query || 'a_' || i::text || ' int default 1'; IF i != num_cols THEN query := query || ', '; END IF; END LOOP; query := query || ')'; EXECUTE format(query); END \$func\$ LANGUAGE plpgsql; EOF for c in $(seq 5 5 30); do for rows in $(seq 1 10); do psql test > /dev/null 2>&1 <<EOF DROP TABLE IF EXISTS to_table; DROP TABLE IF EXISTS from_table; SELECT create_table_cols ('to_table', $c); SELECT create_table_cols ('from_table', $c); INSERT INTO to_table SELECT FROM generate_series(1, $rows * 1000000); COPY to_table TO '$DIR/test.data' WITH (FORMAT text); ALTER TABLE from_table SET ACCESS METHOD blackhole_am; EOF for r in $(seq 1 10); do s=$(psql test -t -A -c "SELECT EXTRACT(EPOCH FROM now())") psql test -c "COPY to_table TO '/dev/null' WITH (FORMAT text)" > /dev/null 2>&1 d=$(psql test -t -A -c "SELECT 1000 * (EXTRACT(EPOCH FROM now()) - $s)") echo "COPY_TO" $c $rows $r $d done # run COPY FROM 10x for r in $(seq 1 10); do s=$(psql test -t -A -c "SELECT EXTRACT(EPOCH FROM now())") psql test -c "COPY from_table FROM '$DIR/test.data' WITH (FORMAT text)" > /dev/null 2>&1 d=$(psql test -t -A -c "SELECT 1000 * (EXTRACT(EPOCH FROM now()) - $s)") echo "COPY_FROM" $c $rows $r $d done done done COPY_TO 5 1 1 212.831000 COPY_TO 5 1 2 208.677000 COPY_TO 5 1 3 215.074000 COPY_TO 5 1 4 218.376000 COPY_TO 5 1 5 219.056000 COPY_TO 5 1 6 218.237000 COPY_TO 5 1 7 234.709000 COPY_TO 5 1 8 220.561000 COPY_TO 5 1 9 219.747000 COPY_TO 5 1 10 211.881000 COPY_FROM 5 1 1 166.336000 COPY_FROM 5 1 2 166.000000 COPY_FROM 5 1 3 166.776000 COPY_FROM 5 1 4 168.493000 COPY_FROM 5 1 5 169.632000 COPY_FROM 5 1 6 164.290000 COPY_FROM 5 1 7 167.841000 COPY_FROM 5 1 8 169.336000 COPY_FROM 5 1 9 172.948000 COPY_FROM 5 1 10 168.893000 COPY_TO 5 2 1 412.065000 COPY_TO 5 2 2 420.758000 COPY_TO 5 2 3 421.387000 COPY_TO 5 2 4 402.165000 COPY_TO 5 2 5 414.407000 COPY_TO 5 2 6 423.387000 COPY_TO 5 2 7 426.431000 COPY_TO 5 2 8 424.798000 COPY_TO 5 2 9 419.588000 COPY_TO 5 2 10 425.688000 COPY_FROM 5 2 1 308.856000 COPY_FROM 5 2 2 319.487000 COPY_FROM 5 2 3 316.488000 COPY_FROM 5 2 4 315.212000 COPY_FROM 5 2 5 316.066000 COPY_FROM 5 2 6 310.381000 COPY_FROM 5 2 7 322.447000 COPY_FROM 5 2 8 318.206000 COPY_FROM 5 2 9 322.588000 COPY_FROM 5 2 10 317.101000 COPY_TO 5 3 1 633.255000 COPY_TO 5 3 2 616.202000 COPY_TO 5 3 3 610.864000 COPY_TO 5 3 4 628.803000 COPY_TO 5 3 5 638.041000 COPY_TO 5 3 6 647.732000 COPY_TO 5 3 7 624.457000 COPY_TO 5 3 8 624.007000 COPY_TO 5 3 9 616.109000 COPY_TO 5 3 10 624.354000 COPY_FROM 5 3 1 469.425000 COPY_FROM 5 3 2 471.284000 COPY_FROM 5 3 3 468.651000 COPY_FROM 5 3 4 465.177000 COPY_FROM 5 3 5 466.697000 COPY_FROM 5 3 6 463.886000 COPY_FROM 5 3 7 480.866000 COPY_FROM 5 3 8 465.048000 COPY_FROM 5 3 9 469.349000 COPY_FROM 5 3 10 467.342000 COPY_TO 5 4 1 837.447000 COPY_TO 5 4 2 848.536000 COPY_TO 5 4 3 867.580000 COPY_TO 5 4 4 831.669000 COPY_TO 5 4 5 839.633000 COPY_TO 5 4 6 846.060000 COPY_TO 5 4 7 824.590000 COPY_TO 5 4 8 836.084000 COPY_TO 5 4 9 845.936000 COPY_TO 5 4 10 851.128000 COPY_FROM 5 4 1 604.809000 COPY_FROM 5 4 2 617.653000 COPY_FROM 5 4 3 615.883000 COPY_FROM 5 4 4 616.633000 COPY_FROM 5 4 5 617.737000 COPY_FROM 5 4 6 617.361000 COPY_FROM 5 4 7 608.998000 COPY_FROM 5 4 8 621.576000 COPY_FROM 5 4 9 619.759000 COPY_FROM 5 4 10 625.312000 COPY_TO 5 5 1 1057.027000 COPY_TO 5 5 2 1038.905000 COPY_TO 5 5 3 1034.425000 COPY_TO 5 5 4 1048.834000 COPY_TO 5 5 5 1069.693000 COPY_TO 5 5 6 1019.558000 COPY_TO 5 5 7 1007.099000 COPY_TO 5 5 8 1021.759000 COPY_TO 5 5 9 1037.773000 COPY_TO 5 5 10 1008.977000 COPY_FROM 5 5 1 753.724000 COPY_FROM 5 5 2 769.060000 COPY_FROM 5 5 3 765.603000 COPY_FROM 5 5 4 769.101000 COPY_FROM 5 5 5 767.057000 COPY_FROM 5 5 6 767.966000 COPY_FROM 5 5 7 781.901000 COPY_FROM 5 5 8 772.262000 COPY_FROM 5 5 9 762.266000 COPY_FROM 5 5 10 767.036000 COPY_TO 5 6 1 1245.932000 COPY_TO 5 6 2 1254.330000 COPY_TO 5 6 3 1254.507000 COPY_TO 5 6 4 1255.708000 COPY_TO 5 6 5 1238.643000 COPY_TO 5 6 6 1259.656000 COPY_TO 5 6 7 1262.356000 COPY_TO 5 6 8 1253.554000 COPY_TO 5 6 9 1262.281000 COPY_TO 5 6 10 1253.491000 COPY_FROM 5 6 1 940.044000 COPY_FROM 5 6 2 938.479000 COPY_FROM 5 6 3 926.584000 COPY_FROM 5 6 4 920.494000 COPY_FROM 5 6 5 908.873000 COPY_FROM 5 6 6 917.936000 COPY_FROM 5 6 7 917.126000 COPY_FROM 5 6 8 921.488000 COPY_FROM 5 6 9 917.245000 COPY_FROM 5 6 10 916.243000 COPY_TO 5 7 1 1430.487000 COPY_TO 5 7 2 1427.373000 COPY_TO 5 7 3 1497.434000 COPY_TO 5 7 4 1463.688000 COPY_TO 5 7 5 1441.485000 COPY_TO 5 7 6 1474.119000 COPY_TO 5 7 7 1514.650000 COPY_TO 5 7 8 1478.208000 COPY_TO 5 7 9 1495.704000 COPY_TO 5 7 10 1459.739000 COPY_FROM 5 7 1 1077.841000 COPY_FROM 5 7 2 1084.081000 COPY_FROM 5 7 3 1093.168000 COPY_FROM 5 7 4 1078.736000 COPY_FROM 5 7 5 1076.685000 COPY_FROM 5 7 6 1110.902000 COPY_FROM 5 7 7 1079.210000 COPY_FROM 5 7 8 1067.793000 COPY_FROM 5 7 9 1079.762000 COPY_FROM 5 7 10 1084.935000 COPY_TO 5 8 1 1650.517000 COPY_TO 5 8 2 1697.566000 COPY_TO 5 8 3 1667.700000 COPY_TO 5 8 4 1656.847000 COPY_TO 5 8 5 1659.966000 COPY_TO 5 8 6 1702.676000 COPY_TO 5 8 7 1696.178000 COPY_TO 5 8 8 1682.269000 COPY_TO 5 8 9 1690.789000 COPY_TO 5 8 10 1703.132000 COPY_FROM 5 8 1 1253.866000 COPY_FROM 5 8 2 1245.838000 COPY_FROM 5 8 3 1231.959000 COPY_FROM 5 8 4 1216.493000 COPY_FROM 5 8 5 1213.282000 COPY_FROM 5 8 6 1213.838000 COPY_FROM 5 8 7 1251.825000 COPY_FROM 5 8 8 1245.100000 COPY_FROM 5 8 9 1261.415000 COPY_FROM 5 8 10 1212.752000 COPY_TO 5 9 1 1850.090000 COPY_TO 5 9 2 1899.929000 COPY_TO 5 9 3 1860.290000 COPY_TO 5 9 4 1832.055000 COPY_TO 5 9 5 1857.414000 COPY_TO 5 9 6 1879.424000 COPY_TO 5 9 7 1875.373000 COPY_TO 5 9 8 1854.969000 COPY_TO 5 9 9 1915.033000 COPY_TO 5 9 10 1866.939000 COPY_FROM 5 9 1 1370.836000 COPY_FROM 5 9 2 1379.806000 COPY_FROM 5 9 3 1372.183000 COPY_FROM 5 9 4 1367.779000 COPY_FROM 5 9 5 1368.464000 COPY_FROM 5 9 6 1380.544000 COPY_FROM 5 9 7 1363.804000 COPY_FROM 5 9 8 1362.463000 COPY_FROM 5 9 9 1371.727000 COPY_FROM 5 9 10 1377.122000 COPY_TO 5 10 1 2058.078000 COPY_TO 5 10 2 2064.015000 COPY_TO 5 10 3 2120.218000 COPY_TO 5 10 4 2060.682000 COPY_TO 5 10 5 2105.438000 COPY_TO 5 10 6 2076.790000 COPY_TO 5 10 7 2095.560000 COPY_TO 5 10 8 2092.245000 COPY_TO 5 10 9 2034.601000 COPY_TO 5 10 10 2094.292000 COPY_FROM 5 10 1 1557.934000 COPY_FROM 5 10 2 1517.610000 COPY_FROM 5 10 3 1506.637000 COPY_FROM 5 10 4 1515.831000 COPY_FROM 5 10 5 1490.391000 COPY_FROM 5 10 6 1507.338000 COPY_FROM 5 10 7 1508.160000 COPY_FROM 5 10 8 1523.402000 COPY_FROM 5 10 9 1504.555000 COPY_FROM 5 10 10 1500.368000 COPY_TO 10 1 1 350.108000 COPY_TO 10 1 2 354.319000 COPY_TO 10 1 3 347.724000 COPY_TO 10 1 4 344.384000 COPY_TO 10 1 5 355.083000 COPY_TO 10 1 6 363.509000 COPY_TO 10 1 7 355.307000 COPY_TO 10 1 8 345.092000 COPY_TO 10 1 9 353.087000 COPY_TO 10 1 10 352.411000 COPY_FROM 10 1 1 259.050000 COPY_FROM 10 1 2 261.272000 COPY_FROM 10 1 3 258.407000 COPY_FROM 10 1 4 260.551000 COPY_FROM 10 1 5 260.306000 COPY_FROM 10 1 6 262.650000 COPY_FROM 10 1 7 259.448000 COPY_FROM 10 1 8 263.050000 COPY_FROM 10 1 9 259.594000 COPY_FROM 10 1 10 262.014000 COPY_TO 10 2 1 687.593000 COPY_TO 10 2 2 689.272000 COPY_TO 10 2 3 672.518000 COPY_TO 10 2 4 697.031000 COPY_TO 10 2 5 709.173000 COPY_TO 10 2 6 704.194000 COPY_TO 10 2 7 696.468000 COPY_TO 10 2 8 693.674000 COPY_TO 10 2 9 699.779000 COPY_TO 10 2 10 692.238000 COPY_FROM 10 2 1 497.979000 COPY_FROM 10 2 2 513.060000 COPY_FROM 10 2 3 502.765000 COPY_FROM 10 2 4 509.832000 COPY_FROM 10 2 5 507.076000 COPY_FROM 10 2 6 501.886000 COPY_FROM 10 2 7 503.953000 COPY_FROM 10 2 8 509.601000 COPY_FROM 10 2 9 508.680000 COPY_FROM 10 2 10 497.768000 COPY_TO 10 3 1 1036.252000 COPY_TO 10 3 2 1011.853000 COPY_TO 10 3 3 1022.256000 COPY_TO 10 3 4 1034.388000 COPY_TO 10 3 5 1011.247000 COPY_TO 10 3 6 1042.124000 COPY_TO 10 3 7 1040.866000 COPY_TO 10 3 8 1025.704000 COPY_TO 10 3 9 1023.673000 COPY_TO 10 3 10 1061.591000 COPY_FROM 10 3 1 733.149000 COPY_FROM 10 3 2 743.642000 COPY_FROM 10 3 3 752.712000 COPY_FROM 10 3 4 735.685000 COPY_FROM 10 3 5 743.496000 COPY_FROM 10 3 6 749.289000 COPY_FROM 10 3 7 747.307000 COPY_FROM 10 3 8 750.439000 COPY_FROM 10 3 9 747.840000 COPY_FROM 10 3 10 746.275000 COPY_TO 10 4 1 1339.158000 COPY_TO 10 4 2 1349.486000 COPY_TO 10 4 3 1391.879000 COPY_TO 10 4 4 1402.481000 COPY_TO 10 4 5 1402.359000 COPY_TO 10 4 6 1349.511000 COPY_TO 10 4 7 1395.431000 COPY_TO 10 4 8 1395.865000 COPY_TO 10 4 9 1352.711000 COPY_TO 10 4 10 1335.961000 COPY_FROM 10 4 1 988.278000 COPY_FROM 10 4 2 988.250000 COPY_FROM 10 4 3 986.391000 COPY_FROM 10 4 4 992.929000 COPY_FROM 10 4 5 984.924000 COPY_FROM 10 4 6 989.783000 COPY_FROM 10 4 7 984.885000 COPY_FROM 10 4 8 977.104000 COPY_FROM 10 4 9 991.286000 COPY_FROM 10 4 10 984.057000 COPY_TO 10 5 1 1714.117000 COPY_TO 10 5 2 1737.382000 COPY_TO 10 5 3 1744.501000 COPY_TO 10 5 4 1705.814000 COPY_TO 10 5 5 1724.631000 COPY_TO 10 5 6 1670.422000 COPY_TO 10 5 7 1724.061000 COPY_TO 10 5 8 1741.960000 COPY_TO 10 5 9 1698.542000 COPY_TO 10 5 10 1703.680000 COPY_FROM 10 5 1 1236.786000 COPY_FROM 10 5 2 1228.271000 COPY_FROM 10 5 3 1233.229000 COPY_FROM 10 5 4 1223.438000 COPY_FROM 10 5 5 1218.269000 COPY_FROM 10 5 6 1215.843000 COPY_FROM 10 5 7 1218.998000 COPY_FROM 10 5 8 1223.761000 COPY_FROM 10 5 9 1237.311000 COPY_FROM 10 5 10 1224.098000 COPY_TO 10 6 1 2034.971000 COPY_TO 10 6 2 2086.575000 COPY_TO 10 6 3 2061.166000 COPY_TO 10 6 4 2028.774000 COPY_TO 10 6 5 1976.820000 COPY_TO 10 6 6 2048.341000 COPY_TO 10 6 7 2126.830000 COPY_TO 10 6 8 2113.916000 COPY_TO 10 6 9 2044.993000 COPY_TO 10 6 10 2059.930000 COPY_FROM 10 6 1 1460.496000 COPY_FROM 10 6 2 1455.160000 COPY_FROM 10 6 3 1472.230000 COPY_FROM 10 6 4 1466.294000 COPY_FROM 10 6 5 1470.005000 COPY_FROM 10 6 6 1460.124000 COPY_FROM 10 6 7 1484.157000 COPY_FROM 10 6 8 1498.308000 COPY_FROM 10 6 9 1472.033000 COPY_FROM 10 6 10 1464.715000 COPY_TO 10 7 1 2392.091000 COPY_TO 10 7 2 2376.371000 COPY_TO 10 7 3 2409.333000 COPY_TO 10 7 4 2436.201000 COPY_TO 10 7 5 2406.606000 COPY_TO 10 7 6 2415.106000 COPY_TO 10 7 7 2460.604000 COPY_TO 10 7 8 2407.684000 COPY_TO 10 7 9 2352.239000 COPY_TO 10 7 10 2453.835000 COPY_FROM 10 7 1 1720.389000 COPY_FROM 10 7 2 1716.569000 COPY_FROM 10 7 3 1724.858000 COPY_FROM 10 7 4 1714.529000 COPY_FROM 10 7 5 1704.039000 COPY_FROM 10 7 6 1723.536000 COPY_FROM 10 7 7 1725.329000 COPY_FROM 10 7 8 1690.714000 COPY_FROM 10 7 9 1726.614000 COPY_FROM 10 7 10 1740.956000 COPY_TO 10 8 1 2796.200000 COPY_TO 10 8 2 2761.445000 COPY_TO 10 8 3 2753.313000 COPY_TO 10 8 4 2767.549000 COPY_TO 10 8 5 2759.920000 COPY_TO 10 8 6 2753.090000 COPY_TO 10 8 7 2766.374000 COPY_TO 10 8 8 2758.385000 COPY_TO 10 8 9 2822.724000 COPY_TO 10 8 10 2746.903000 COPY_FROM 10 8 1 1963.436000 COPY_FROM 10 8 2 1965.409000 COPY_FROM 10 8 3 1978.345000 COPY_FROM 10 8 4 1957.258000 COPY_FROM 10 8 5 1948.144000 COPY_FROM 10 8 6 1960.546000 COPY_FROM 10 8 7 1985.631000 COPY_FROM 10 8 8 1928.848000 COPY_FROM 10 8 9 1932.803000 COPY_FROM 10 8 10 1950.939000 COPY_TO 10 9 1 3101.821000 COPY_TO 10 9 2 3119.955000 COPY_TO 10 9 3 3071.974000 COPY_TO 10 9 4 3058.962000 COPY_TO 10 9 5 3100.206000 COPY_TO 10 9 6 3085.071000 COPY_TO 10 9 7 3099.553000 COPY_TO 10 9 8 3133.255000 COPY_TO 10 9 9 3112.448000 COPY_TO 10 9 10 3078.218000 COPY_FROM 10 9 1 2231.402000 COPY_FROM 10 9 2 2270.319000 COPY_FROM 10 9 3 2212.585000 COPY_FROM 10 9 4 2214.820000 COPY_FROM 10 9 5 2182.791000 COPY_FROM 10 9 6 2297.706000 COPY_FROM 10 9 7 2203.188000 COPY_FROM 10 9 8 2275.390000 COPY_FROM 10 9 9 2191.008000 COPY_FROM 10 9 10 2202.305000 COPY_TO 10 10 1 3451.928000 COPY_TO 10 10 2 3434.760000 COPY_TO 10 10 3 3473.176000 COPY_TO 10 10 4 3483.251000 COPY_TO 10 10 5 3432.006000 COPY_TO 10 10 6 3423.612000 COPY_TO 10 10 7 3444.291000 COPY_TO 10 10 8 3425.827000 COPY_TO 10 10 9 3411.279000 COPY_TO 10 10 10 3454.997000 COPY_FROM 10 10 1 2447.611000 COPY_FROM 10 10 2 2462.314000 COPY_FROM 10 10 3 2435.629000 COPY_FROM 10 10 4 2536.588000 COPY_FROM 10 10 5 2472.294000 COPY_FROM 10 10 6 2461.858000 COPY_FROM 10 10 7 2451.032000 COPY_FROM 10 10 8 2430.200000 COPY_FROM 10 10 9 2493.472000 COPY_FROM 10 10 10 2473.227000 COPY_TO 15 1 1 484.336000 COPY_TO 15 1 2 479.033000 COPY_TO 15 1 3 479.735000 COPY_TO 15 1 4 485.839000 COPY_TO 15 1 5 478.669000 COPY_TO 15 1 6 503.063000 COPY_TO 15 1 7 497.116000 COPY_TO 15 1 8 496.381000 COPY_TO 15 1 9 492.082000 COPY_TO 15 1 10 503.341000 COPY_FROM 15 1 1 344.944000 COPY_FROM 15 1 2 347.820000 COPY_FROM 15 1 3 350.856000 COPY_FROM 15 1 4 343.687000 COPY_FROM 15 1 5 348.509000 COPY_FROM 15 1 6 352.488000 COPY_FROM 15 1 7 347.183000 COPY_FROM 15 1 8 346.262000 COPY_FROM 15 1 9 346.139000 COPY_FROM 15 1 10 349.911000 COPY_TO 15 2 1 962.932000 COPY_TO 15 2 2 963.658000 COPY_TO 15 2 3 962.137000 COPY_TO 15 2 4 962.108000 COPY_TO 15 2 5 970.632000 COPY_TO 15 2 6 953.700000 COPY_TO 15 2 7 981.138000 COPY_TO 15 2 8 973.898000 COPY_TO 15 2 9 970.741000 COPY_TO 15 2 10 948.693000 COPY_FROM 15 2 1 665.328000 COPY_FROM 15 2 2 676.310000 COPY_FROM 15 2 3 671.458000 COPY_FROM 15 2 4 670.664000 COPY_FROM 15 2 5 679.016000 COPY_FROM 15 2 6 669.444000 COPY_FROM 15 2 7 667.946000 COPY_FROM 15 2 8 667.764000 COPY_FROM 15 2 9 674.499000 COPY_FROM 15 2 10 671.073000 COPY_TO 15 3 1 1420.086000 COPY_TO 15 3 2 1448.308000 COPY_TO 15 3 3 1454.913000 COPY_TO 15 3 4 1439.704000 COPY_TO 15 3 5 1446.298000 COPY_TO 15 3 6 1470.852000 COPY_TO 15 3 7 1456.382000 COPY_TO 15 3 8 1458.444000 COPY_TO 15 3 9 1463.900000 COPY_TO 15 3 10 1461.039000 COPY_FROM 15 3 1 1007.392000 COPY_FROM 15 3 2 1001.408000 COPY_FROM 15 3 3 1010.905000 COPY_FROM 15 3 4 1004.810000 COPY_FROM 15 3 5 1000.949000 COPY_FROM 15 3 6 1011.789000 COPY_FROM 15 3 7 1009.358000 COPY_FROM 15 3 8 1010.479000 COPY_FROM 15 3 9 999.429000 COPY_FROM 15 3 10 1021.780000 COPY_TO 15 4 1 1933.560000 COPY_TO 15 4 2 1903.286000 COPY_TO 15 4 3 1931.942000 COPY_TO 15 4 4 1963.633000 COPY_TO 15 4 5 1936.343000 COPY_TO 15 4 6 1944.860000 COPY_TO 15 4 7 1924.043000 COPY_TO 15 4 8 1923.471000 COPY_TO 15 4 9 1890.255000 COPY_TO 15 4 10 1960.720000 COPY_FROM 15 4 1 1368.884000 COPY_FROM 15 4 2 1339.628000 COPY_FROM 15 4 3 1311.420000 COPY_FROM 15 4 4 1334.597000 COPY_FROM 15 4 5 1351.018000 COPY_FROM 15 4 6 1344.878000 COPY_FROM 15 4 7 1355.443000 COPY_FROM 15 4 8 1339.170000 COPY_FROM 15 4 9 1340.700000 COPY_FROM 15 4 10 1326.241000 COPY_TO 15 5 1 2318.844000 COPY_TO 15 5 2 2402.419000 COPY_TO 15 5 3 2396.781000 COPY_TO 15 5 4 2423.735000 COPY_TO 15 5 5 2409.499000 COPY_TO 15 5 6 2428.851000 COPY_TO 15 5 7 2348.598000 COPY_TO 15 5 8 2322.848000 COPY_TO 15 5 9 2352.590000 COPY_TO 15 5 10 2445.076000 COPY_FROM 15 5 1 1648.351000 COPY_FROM 15 5 2 1651.210000 COPY_FROM 15 5 3 1652.014000 COPY_FROM 15 5 4 1657.594000 COPY_FROM 15 5 5 1664.718000 COPY_FROM 15 5 6 1659.842000 COPY_FROM 15 5 7 1624.535000 COPY_FROM 15 5 8 1674.149000 COPY_FROM 15 5 9 1647.591000 COPY_FROM 15 5 10 1669.124000 COPY_TO 15 6 1 2881.853000 COPY_TO 15 6 2 2868.673000 COPY_TO 15 6 3 2965.197000 COPY_TO 15 6 4 2884.662000 COPY_TO 15 6 5 2838.135000 COPY_TO 15 6 6 2916.165000 COPY_TO 15 6 7 2886.197000 COPY_TO 15 6 8 2933.154000 COPY_TO 15 6 9 2928.349000 COPY_TO 15 6 10 2901.545000 COPY_FROM 15 6 1 2013.017000 COPY_FROM 15 6 2 1978.835000 COPY_FROM 15 6 3 2004.485000 COPY_FROM 15 6 4 1987.586000 COPY_FROM 15 6 5 1975.135000 COPY_FROM 15 6 6 1989.522000 COPY_FROM 15 6 7 1988.856000 COPY_FROM 15 6 8 1983.815000 COPY_FROM 15 6 9 2013.150000 COPY_FROM 15 6 10 1997.074000 COPY_TO 15 7 1 3335.493000 COPY_TO 15 7 2 3357.154000 COPY_TO 15 7 3 3347.085000 COPY_TO 15 7 4 3296.994000 COPY_TO 15 7 5 3376.383000 COPY_TO 15 7 6 3368.554000 COPY_TO 15 7 7 3401.287000 COPY_TO 15 7 8 3359.792000 COPY_TO 15 7 9 3351.542000 COPY_TO 15 7 10 3359.085000 COPY_FROM 15 7 1 2341.669000 COPY_FROM 15 7 2 2318.762000 COPY_FROM 15 7 3 2302.094000 COPY_FROM 15 7 4 2295.824000 COPY_FROM 15 7 5 2282.052000 COPY_FROM 15 7 6 2285.734000 COPY_FROM 15 7 7 2286.871000 COPY_FROM 15 7 8 2301.570000 COPY_FROM 15 7 9 2294.122000 COPY_FROM 15 7 10 2318.100000 COPY_TO 15 8 1 3838.944000 COPY_TO 15 8 2 3832.013000 COPY_TO 15 8 3 3794.855000 COPY_TO 15 8 4 3829.692000 COPY_TO 15 8 5 3902.267000 COPY_TO 15 8 6 3876.061000 COPY_TO 15 8 7 3844.652000 COPY_TO 15 8 8 3819.619000 COPY_TO 15 8 9 3891.511000 COPY_TO 15 8 10 3902.708000 COPY_FROM 15 8 1 2665.396000 COPY_FROM 15 8 2 2677.914000 COPY_FROM 15 8 3 2666.726000 COPY_FROM 15 8 4 2633.747000 COPY_FROM 15 8 5 2632.702000 COPY_FROM 15 8 6 2664.116000 COPY_FROM 15 8 7 2614.453000 COPY_FROM 15 8 8 2662.111000 COPY_FROM 15 8 9 2660.616000 COPY_FROM 15 8 10 2695.048000 COPY_TO 15 9 1 4341.815000 COPY_TO 15 9 2 4302.586000 COPY_TO 15 9 3 4281.296000 COPY_TO 15 9 4 4260.384000 COPY_TO 15 9 5 4354.295000 COPY_TO 15 9 6 4395.239000 COPY_TO 15 9 7 4294.927000 COPY_TO 15 9 8 4299.131000 COPY_TO 15 9 9 4324.381000 COPY_TO 15 9 10 4308.416000 COPY_FROM 15 9 1 2952.762000 COPY_FROM 15 9 2 2976.541000 COPY_FROM 15 9 3 2980.895000 COPY_FROM 15 9 4 2988.607000 COPY_FROM 15 9 5 2931.639000 COPY_FROM 15 9 6 2980.360000 COPY_FROM 15 9 7 2987.142000 COPY_FROM 15 9 8 2942.020000 COPY_FROM 15 9 9 2956.429000 COPY_FROM 15 9 10 2976.833000 COPY_TO 15 10 1 4908.128000 COPY_TO 15 10 2 4808.306000 COPY_TO 15 10 3 4884.962000 COPY_TO 15 10 4 4871.861000 COPY_TO 15 10 5 4793.649000 COPY_TO 15 10 6 4783.691000 COPY_TO 15 10 7 4953.107000 COPY_TO 15 10 8 4770.645000 COPY_TO 15 10 9 4830.319000 COPY_TO 15 10 10 4817.374000 COPY_FROM 15 10 1 3316.914000 COPY_FROM 15 10 2 3317.386000 COPY_FROM 15 10 3 3304.798000 COPY_FROM 15 10 4 3260.573000 COPY_FROM 15 10 5 3275.390000 COPY_FROM 15 10 6 3298.207000 COPY_FROM 15 10 7 3286.026000 COPY_FROM 15 10 8 3363.954000 COPY_FROM 15 10 9 3294.820000 COPY_FROM 15 10 10 3306.407000 COPY_TO 20 1 1 619.998000 COPY_TO 20 1 2 616.942000 COPY_TO 20 1 3 624.587000 COPY_TO 20 1 4 633.838000 COPY_TO 20 1 5 651.659000 COPY_TO 20 1 6 638.405000 COPY_TO 20 1 7 629.828000 COPY_TO 20 1 8 621.210000 COPY_TO 20 1 9 635.503000 COPY_TO 20 1 10 629.262000 COPY_FROM 20 1 1 433.467000 COPY_FROM 20 1 2 431.611000 COPY_FROM 20 1 3 438.673000 COPY_FROM 20 1 4 439.864000 COPY_FROM 20 1 5 436.883000 COPY_FROM 20 1 6 436.025000 COPY_FROM 20 1 7 447.105000 COPY_FROM 20 1 8 452.754000 COPY_FROM 20 1 9 434.757000 COPY_FROM 20 1 10 439.372000 COPY_TO 20 2 1 1215.557000 COPY_TO 20 2 2 1198.834000 COPY_TO 20 2 3 1248.734000 COPY_TO 20 2 4 1224.716000 COPY_TO 20 2 5 1221.355000 COPY_TO 20 2 6 1235.157000 COPY_TO 20 2 7 1213.212000 COPY_TO 20 2 8 1251.544000 COPY_TO 20 2 9 1211.466000 COPY_TO 20 2 10 1232.067000 COPY_FROM 20 2 1 853.265000 COPY_FROM 20 2 2 861.634000 COPY_FROM 20 2 3 875.109000 COPY_FROM 20 2 4 866.576000 COPY_FROM 20 2 5 869.608000 COPY_FROM 20 2 6 867.634000 COPY_FROM 20 2 7 868.359000 COPY_FROM 20 2 8 879.867000 COPY_FROM 20 2 9 856.513000 COPY_FROM 20 2 10 846.929000 COPY_TO 20 3 1 1853.167000 COPY_TO 20 3 2 1908.958000 COPY_TO 20 3 3 1854.300000 COPY_TO 20 3 4 1854.920000 COPY_TO 20 3 5 1908.171000 COPY_TO 20 3 6 1875.182000 COPY_TO 20 3 7 1858.945000 COPY_TO 20 3 8 1836.676000 COPY_TO 20 3 9 1892.760000 COPY_TO 20 3 10 1832.188000 COPY_FROM 20 3 1 1269.621000 COPY_FROM 20 3 2 1268.794000 COPY_FROM 20 3 3 1306.010000 COPY_FROM 20 3 4 1268.746000 COPY_FROM 20 3 5 1285.443000 COPY_FROM 20 3 6 1272.459000 COPY_FROM 20 3 7 1284.552000 COPY_FROM 20 3 8 1277.634000 COPY_FROM 20 3 9 1283.592000 COPY_FROM 20 3 10 1277.291000 COPY_TO 20 4 1 2366.791000 COPY_TO 20 4 2 2467.617000 COPY_TO 20 4 3 2503.922000 COPY_TO 20 4 4 2419.396000 COPY_TO 20 4 5 2362.517000 COPY_TO 20 4 6 2436.106000 COPY_TO 20 4 7 2515.537000 COPY_TO 20 4 8 2444.051000 COPY_TO 20 4 9 2368.470000 COPY_TO 20 4 10 2476.241000 COPY_FROM 20 4 1 1686.377000 COPY_FROM 20 4 2 1766.247000 COPY_FROM 20 4 3 1765.013000 COPY_FROM 20 4 4 1710.638000 COPY_FROM 20 4 5 1681.944000 COPY_FROM 20 4 6 1672.305000 COPY_FROM 20 4 7 1680.594000 COPY_FROM 20 4 8 1692.007000 COPY_FROM 20 4 9 1696.334000 COPY_FROM 20 4 10 1673.502000 COPY_TO 20 5 1 3044.926000 COPY_TO 20 5 2 2999.139000 COPY_TO 20 5 3 3012.201000 COPY_TO 20 5 4 3079.507000 COPY_TO 20 5 5 3084.210000 COPY_TO 20 5 6 3106.328000 COPY_TO 20 5 7 3107.643000 COPY_TO 20 5 8 3103.127000 COPY_TO 20 5 9 3098.074000 COPY_TO 20 5 10 3071.407000 COPY_FROM 20 5 1 2110.909000 COPY_FROM 20 5 2 2119.924000 COPY_FROM 20 5 3 2094.429000 COPY_FROM 20 5 4 2113.787000 COPY_FROM 20 5 5 2093.251000 COPY_FROM 20 5 6 2103.724000 COPY_FROM 20 5 7 2163.264000 COPY_FROM 20 5 8 2110.832000 COPY_FROM 20 5 9 2120.593000 COPY_FROM 20 5 10 2108.865000 COPY_TO 20 6 1 3778.026000 COPY_TO 20 6 2 3660.842000 COPY_TO 20 6 3 3586.255000 COPY_TO 20 6 4 3621.287000 COPY_TO 20 6 5 3765.054000 COPY_TO 20 6 6 3730.942000 COPY_TO 20 6 7 3700.704000 COPY_TO 20 6 8 3683.990000 COPY_TO 20 6 9 3654.364000 COPY_TO 20 6 10 3711.707000 COPY_FROM 20 6 1 2512.796000 COPY_FROM 20 6 2 2499.849000 COPY_FROM 20 6 3 2581.643000 COPY_FROM 20 6 4 2540.972000 COPY_FROM 20 6 5 2522.357000 COPY_FROM 20 6 6 2519.327000 COPY_FROM 20 6 7 2539.536000 COPY_FROM 20 6 8 2529.492000 COPY_FROM 20 6 9 2527.186000 COPY_FROM 20 6 10 2537.575000 COPY_TO 20 7 1 4302.273000 COPY_TO 20 7 2 4320.033000 COPY_TO 20 7 3 4234.169000 COPY_TO 20 7 4 4347.949000 COPY_TO 20 7 5 4297.509000 COPY_TO 20 7 6 4348.086000 COPY_TO 20 7 7 4302.051000 COPY_TO 20 7 8 4325.364000 COPY_TO 20 7 9 4322.654000 COPY_TO 20 7 10 4271.526000 COPY_FROM 20 7 1 2911.560000 COPY_FROM 20 7 2 2940.254000 COPY_FROM 20 7 3 2980.597000 COPY_FROM 20 7 4 2973.070000 COPY_FROM 20 7 5 2933.554000 COPY_FROM 20 7 6 2953.611000 COPY_FROM 20 7 7 2922.042000 COPY_FROM 20 7 8 2906.997000 COPY_FROM 20 7 9 2904.686000 COPY_FROM 20 7 10 2941.453000 COPY_TO 20 8 1 4764.222000 COPY_TO 20 8 2 4728.320000 COPY_TO 20 8 3 4795.743000 COPY_TO 20 8 4 4882.833000 COPY_TO 20 8 5 4815.518000 COPY_TO 20 8 6 4886.483000 COPY_TO 20 8 7 4924.319000 COPY_TO 20 8 8 4838.255000 COPY_TO 20 8 9 4863.534000 COPY_TO 20 8 10 4925.173000 COPY_FROM 20 8 1 3377.310000 COPY_FROM 20 8 2 3374.520000 COPY_FROM 20 8 3 3415.924000 COPY_FROM 20 8 4 3359.085000 COPY_FROM 20 8 5 3354.984000 COPY_FROM 20 8 6 3314.657000 COPY_FROM 20 8 7 3315.929000 COPY_FROM 20 8 8 3446.995000 COPY_FROM 20 8 9 3368.091000 COPY_FROM 20 8 10 3390.674000 COPY_TO 20 9 1 5463.960000 COPY_TO 20 9 2 5463.921000 COPY_TO 20 9 3 5378.138000 COPY_TO 20 9 4 5535.958000 COPY_TO 20 9 5 5503.000000 COPY_TO 20 9 6 5457.850000 COPY_TO 20 9 7 5435.157000 COPY_TO 20 9 8 5422.457000 COPY_TO 20 9 9 5482.427000 COPY_TO 20 9 10 5495.809000 COPY_FROM 20 9 1 3876.496000 COPY_FROM 20 9 2 3770.921000 COPY_FROM 20 9 3 3729.432000 COPY_FROM 20 9 4 3739.708000 COPY_FROM 20 9 5 3787.856000 COPY_FROM 20 9 6 3757.324000 COPY_FROM 20 9 7 3793.676000 COPY_FROM 20 9 8 3840.151000 COPY_FROM 20 9 9 3721.829000 COPY_FROM 20 9 10 3769.584000 COPY_TO 20 10 1 6021.466000 COPY_TO 20 10 2 6050.644000 COPY_TO 20 10 3 6035.796000 COPY_TO 20 10 4 5991.765000 COPY_TO 20 10 5 6095.925000 COPY_TO 20 10 6 6006.453000 COPY_TO 20 10 7 6043.915000 COPY_TO 20 10 8 6184.330000 COPY_TO 20 10 9 5997.352000 COPY_TO 20 10 10 6142.882000 COPY_FROM 20 10 1 4220.218000 COPY_FROM 20 10 2 4160.915000 COPY_FROM 20 10 3 4172.628000 COPY_FROM 20 10 4 4183.532000 COPY_FROM 20 10 5 4208.204000 COPY_FROM 20 10 6 4232.293000 COPY_FROM 20 10 7 4188.968000 COPY_FROM 20 10 8 4191.494000 COPY_FROM 20 10 9 4196.841000 COPY_FROM 20 10 10 4172.418000 COPY_TO 25 1 1 774.678000 COPY_TO 25 1 2 787.791000 COPY_TO 25 1 3 773.815000 COPY_TO 25 1 4 744.220000 COPY_TO 25 1 5 763.742000 COPY_TO 25 1 6 764.779000 COPY_TO 25 1 7 763.397000 COPY_TO 25 1 8 750.529000 COPY_TO 25 1 9 775.028000 COPY_TO 25 1 10 763.085000 COPY_FROM 25 1 1 524.445000 COPY_FROM 25 1 2 519.951000 COPY_FROM 25 1 3 516.212000 COPY_FROM 25 1 4 516.155000 COPY_FROM 25 1 5 519.686000 COPY_FROM 25 1 6 524.260000 COPY_FROM 25 1 7 521.384000 COPY_FROM 25 1 8 516.947000 COPY_FROM 25 1 9 516.268000 COPY_FROM 25 1 10 513.815000 COPY_TO 25 2 1 1513.097000 COPY_TO 25 2 2 1516.435000 COPY_TO 25 2 3 1514.322000 COPY_TO 25 2 4 1515.332000 COPY_TO 25 2 5 1539.159000 COPY_TO 25 2 6 1504.517000 COPY_TO 25 2 7 1551.701000 COPY_TO 25 2 8 1536.408000 COPY_TO 25 2 9 1506.469000 COPY_TO 25 2 10 1507.693000 COPY_FROM 25 2 1 1031.906000 COPY_FROM 25 2 2 1011.518000 COPY_FROM 25 2 3 1015.601000 COPY_FROM 25 2 4 1022.738000 COPY_FROM 25 2 5 1024.219000 COPY_FROM 25 2 6 1018.943000 COPY_FROM 25 2 7 1008.076000 COPY_FROM 25 2 8 1008.687000 COPY_FROM 25 2 9 1019.874000 COPY_FROM 25 2 10 1010.362000 COPY_TO 25 3 1 2275.840000 COPY_TO 25 3 2 2292.456000 COPY_TO 25 3 3 2304.261000 COPY_TO 25 3 4 2260.663000 COPY_TO 25 3 5 2274.911000 COPY_TO 25 3 6 2307.456000 COPY_TO 25 3 7 2304.885000 COPY_TO 25 3 8 2328.952000 COPY_TO 25 3 9 2205.891000 COPY_TO 25 3 10 2252.140000 COPY_FROM 25 3 1 1491.799000 COPY_FROM 25 3 2 1508.012000 COPY_FROM 25 3 3 1507.554000 COPY_FROM 25 3 4 1540.556000 COPY_FROM 25 3 5 1538.755000 COPY_FROM 25 3 6 1524.962000 COPY_FROM 25 3 7 1519.040000 COPY_FROM 25 3 8 1527.385000 COPY_FROM 25 3 9 1542.953000 COPY_FROM 25 3 10 1523.412000 COPY_TO 25 4 1 3052.605000 COPY_TO 25 4 2 2998.820000 COPY_TO 25 4 3 2984.156000 COPY_TO 25 4 4 3034.054000 COPY_TO 25 4 5 3035.638000 COPY_TO 25 4 6 3021.914000 COPY_TO 25 4 7 3086.029000 COPY_TO 25 4 8 3104.967000 COPY_TO 25 4 9 3084.419000 COPY_TO 25 4 10 3052.696000 COPY_FROM 25 4 1 2019.843000 COPY_FROM 25 4 2 2010.303000 COPY_FROM 25 4 3 2008.544000 COPY_FROM 25 4 4 2017.551000 COPY_FROM 25 4 5 1983.106000 COPY_FROM 25 4 6 1972.640000 COPY_FROM 25 4 7 1998.370000 COPY_FROM 25 4 8 1972.399000 COPY_FROM 25 4 9 2014.721000 COPY_FROM 25 4 10 1990.860000 COPY_TO 25 5 1 3803.703000 COPY_TO 25 5 2 3801.972000 COPY_TO 25 5 3 3732.563000 COPY_TO 25 5 4 3844.295000 COPY_TO 25 5 5 3843.996000 COPY_TO 25 5 6 3860.533000 COPY_TO 25 5 7 3885.893000 COPY_TO 25 5 8 3901.853000 COPY_TO 25 5 9 3811.751000 COPY_TO 25 5 10 3830.153000 COPY_FROM 25 5 1 2512.122000 COPY_FROM 25 5 2 2485.190000 COPY_FROM 25 5 3 2514.064000 COPY_FROM 25 5 4 2629.482000 COPY_FROM 25 5 5 2574.073000 COPY_FROM 25 5 6 2554.008000 COPY_FROM 25 5 7 2554.302000 COPY_FROM 25 5 8 2538.815000 COPY_FROM 25 5 9 2557.007000 COPY_FROM 25 5 10 2498.580000 COPY_TO 25 6 1 4623.929000 COPY_TO 25 6 2 4565.644000 COPY_TO 25 6 3 4579.721000 COPY_TO 25 6 4 4524.352000 COPY_TO 25 6 5 4470.642000 COPY_TO 25 6 6 4563.316000 COPY_TO 25 6 7 4576.716000 COPY_TO 25 6 8 4491.117000 COPY_TO 25 6 9 4544.761000 COPY_TO 25 6 10 4424.612000 COPY_FROM 25 6 1 3018.827000 COPY_FROM 25 6 2 2978.490000 COPY_FROM 25 6 3 2995.232000 COPY_FROM 25 6 4 2967.654000 COPY_FROM 25 6 5 3029.289000 COPY_FROM 25 6 6 2956.739000 COPY_FROM 25 6 7 2964.034000 COPY_FROM 25 6 8 2969.406000 COPY_FROM 25 6 9 2990.859000 COPY_FROM 25 6 10 3004.016000 COPY_TO 25 7 1 5388.767000 COPY_TO 25 7 2 5261.497000 COPY_TO 25 7 3 5266.503000 COPY_TO 25 7 4 5328.781000 COPY_TO 25 7 5 5331.428000 COPY_TO 25 7 6 5342.277000 COPY_TO 25 7 7 5309.748000 COPY_TO 25 7 8 5396.271000 COPY_TO 25 7 9 5242.006000 COPY_TO 25 7 10 5204.319000 COPY_FROM 25 7 1 3526.509000 COPY_FROM 25 7 2 3533.526000 COPY_FROM 25 7 3 3574.351000 COPY_FROM 25 7 4 3550.997000 COPY_FROM 25 7 5 3519.623000 COPY_FROM 25 7 6 3462.743000 COPY_FROM 25 7 7 3504.243000 COPY_FROM 25 7 8 3521.010000 COPY_FROM 25 7 9 3431.482000 COPY_FROM 25 7 10 3419.169000 COPY_TO 25 8 1 6097.554000 COPY_TO 25 8 2 5984.897000 COPY_TO 25 8 3 6040.903000 COPY_TO 25 8 4 6147.806000 COPY_TO 25 8 5 6037.164000 COPY_TO 25 8 6 5987.661000 COPY_TO 25 8 7 6096.899000 COPY_TO 25 8 8 6073.973000 COPY_TO 25 8 9 6105.735000 COPY_TO 25 8 10 5974.114000 COPY_FROM 25 8 1 3988.738000 COPY_FROM 25 8 2 4009.777000 COPY_FROM 25 8 3 4027.431000 COPY_FROM 25 8 4 3976.333000 COPY_FROM 25 8 5 3961.928000 COPY_FROM 25 8 6 3974.345000 COPY_FROM 25 8 7 4029.581000 COPY_FROM 25 8 8 4025.947000 COPY_FROM 25 8 9 3977.926000 COPY_FROM 25 8 10 4035.786000 COPY_TO 25 9 1 6753.774000 COPY_TO 25 9 2 6700.288000 COPY_TO 25 9 3 6880.717000 COPY_TO 25 9 4 6825.173000 COPY_TO 25 9 5 6697.153000 COPY_TO 25 9 6 6785.494000 COPY_TO 25 9 7 6879.979000 COPY_TO 25 9 8 6743.111000 COPY_TO 25 9 9 6850.346000 COPY_TO 25 9 10 6787.185000 COPY_FROM 25 9 1 4517.219000 COPY_FROM 25 9 2 4531.329000 COPY_FROM 25 9 3 4529.439000 COPY_FROM 25 9 4 4481.905000 COPY_FROM 25 9 5 4518.109000 COPY_FROM 25 9 6 4502.731000 COPY_FROM 25 9 7 4473.914000 COPY_FROM 25 9 8 4471.436000 COPY_FROM 25 9 9 4500.187000 COPY_FROM 25 9 10 4479.554000 COPY_TO 25 10 1 7557.810000 COPY_TO 25 10 2 7559.711000 COPY_TO 25 10 3 7542.392000 COPY_TO 25 10 4 7291.018000 COPY_TO 25 10 5 7504.865000 COPY_TO 25 10 6 7432.488000 COPY_TO 25 10 7 7432.530000 COPY_TO 25 10 8 7474.229000 COPY_TO 25 10 9 7384.188000 COPY_TO 25 10 10 7551.992000 COPY_FROM 25 10 1 4964.734000 COPY_FROM 25 10 2 5042.329000 COPY_FROM 25 10 3 5013.357000 COPY_FROM 25 10 4 4986.712000 COPY_FROM 25 10 5 4996.862000 COPY_FROM 25 10 6 4945.983000 COPY_FROM 25 10 7 4994.463000 COPY_FROM 25 10 8 4944.533000 COPY_FROM 25 10 9 5018.457000 COPY_FROM 25 10 10 4967.123000 COPY_TO 30 1 1 905.785000 COPY_TO 30 1 2 919.553000 COPY_TO 30 1 3 891.263000 COPY_TO 30 1 4 923.963000 COPY_TO 30 1 5 901.843000 COPY_TO 30 1 6 915.491000 COPY_TO 30 1 7 896.540000 COPY_TO 30 1 8 906.324000 COPY_TO 30 1 9 892.686000 COPY_TO 30 1 10 924.998000 COPY_FROM 30 1 1 587.472000 COPY_FROM 30 1 2 605.176000 COPY_FROM 30 1 3 591.641000 COPY_FROM 30 1 4 622.076000 COPY_FROM 30 1 5 604.110000 COPY_FROM 30 1 6 619.221000 COPY_FROM 30 1 7 612.524000 COPY_FROM 30 1 8 603.729000 COPY_FROM 30 1 9 595.670000 COPY_FROM 30 1 10 598.395000 COPY_TO 30 2 1 1799.114000 COPY_TO 30 2 2 1802.407000 COPY_TO 30 2 3 1813.957000 COPY_TO 30 2 4 1765.727000 COPY_TO 30 2 5 1798.418000 COPY_TO 30 2 6 1817.917000 COPY_TO 30 2 7 1780.496000 COPY_TO 30 2 8 1772.734000 COPY_TO 30 2 9 1771.637000 COPY_TO 30 2 10 1837.537000 COPY_FROM 30 2 1 1186.556000 COPY_FROM 30 2 2 1189.396000 COPY_FROM 30 2 3 1188.794000 COPY_FROM 30 2 4 1196.751000 COPY_FROM 30 2 5 1208.097000 COPY_FROM 30 2 6 1195.639000 COPY_FROM 30 2 7 1181.028000 COPY_FROM 30 2 8 1177.701000 COPY_FROM 30 2 9 1181.959000 COPY_FROM 30 2 10 1171.377000 COPY_TO 30 3 1 2668.510000 COPY_TO 30 3 2 2662.493000 COPY_TO 30 3 3 2659.467000 COPY_TO 30 3 4 2629.276000 COPY_TO 30 3 5 2630.829000 COPY_TO 30 3 6 2632.760000 COPY_TO 30 3 7 2642.559000 COPY_TO 30 3 8 2675.854000 COPY_TO 30 3 9 2686.168000 COPY_TO 30 3 10 2703.022000 COPY_FROM 30 3 1 1749.300000 COPY_FROM 30 3 2 1732.106000 COPY_FROM 30 3 3 1744.452000 COPY_FROM 30 3 4 1762.979000 COPY_FROM 30 3 5 1758.033000 COPY_FROM 30 3 6 1772.605000 COPY_FROM 30 3 7 1754.809000 COPY_FROM 30 3 8 1751.785000 COPY_FROM 30 3 9 1762.331000 COPY_FROM 30 3 10 1745.872000 COPY_TO 30 4 1 3575.638000 COPY_TO 30 4 2 3540.611000 COPY_TO 30 4 3 3555.631000 COPY_TO 30 4 4 3508.023000 COPY_TO 30 4 5 3548.267000 COPY_TO 30 4 6 3530.229000 COPY_TO 30 4 7 3624.151000 COPY_TO 30 4 8 3549.913000 COPY_TO 30 4 9 3579.071000 COPY_TO 30 4 10 3548.049000 COPY_FROM 30 4 1 2333.686000 COPY_FROM 30 4 2 2354.055000 COPY_FROM 30 4 3 2329.804000 COPY_FROM 30 4 4 2393.154000 COPY_FROM 30 4 5 2357.848000 COPY_FROM 30 4 6 2351.915000 COPY_FROM 30 4 7 2340.428000 COPY_FROM 30 4 8 2364.307000 COPY_FROM 30 4 9 2353.620000 COPY_FROM 30 4 10 2363.992000 COPY_TO 30 5 1 4324.843000 COPY_TO 30 5 2 4387.595000 COPY_TO 30 5 3 4416.761000 COPY_TO 30 5 4 4406.291000 COPY_TO 30 5 5 4418.657000 COPY_TO 30 5 6 4432.811000 COPY_TO 30 5 7 4422.989000 COPY_TO 30 5 8 4467.277000 COPY_TO 30 5 9 4474.720000 COPY_TO 30 5 10 4419.907000 COPY_FROM 30 5 1 2911.757000 COPY_FROM 30 5 2 2921.622000 COPY_FROM 30 5 3 2863.662000 COPY_FROM 30 5 4 3017.345000 COPY_FROM 30 5 5 2904.579000 COPY_FROM 30 5 6 2954.328000 COPY_FROM 30 5 7 2965.111000 COPY_FROM 30 5 8 2962.503000 COPY_FROM 30 5 9 2881.468000 COPY_FROM 30 5 10 2932.883000 COPY_TO 30 6 1 5324.111000 COPY_TO 30 6 2 5273.693000 COPY_TO 30 6 3 5477.630000 COPY_TO 30 6 4 5470.590000 COPY_TO 30 6 5 5330.046000 COPY_TO 30 6 6 5314.785000 COPY_TO 30 6 7 5280.238000 COPY_TO 30 6 8 5447.156000 COPY_TO 30 6 9 5470.025000 COPY_TO 30 6 10 5382.615000 COPY_FROM 30 6 1 3519.835000 COPY_FROM 30 6 2 3495.999000 COPY_FROM 30 6 3 3447.579000 COPY_FROM 30 6 4 3503.293000 COPY_FROM 30 6 5 3467.442000 COPY_FROM 30 6 6 3502.490000 COPY_FROM 30 6 7 3539.083000 COPY_FROM 30 6 8 3514.108000 COPY_FROM 30 6 9 3558.769000 COPY_FROM 30 6 10 3557.883000 COPY_TO 30 7 1 6270.765000 COPY_TO 30 7 2 6250.630000 COPY_TO 30 7 3 6291.501000 COPY_TO 30 7 4 6277.021000 COPY_TO 30 7 5 6197.067000 COPY_TO 30 7 6 6204.168000 COPY_TO 30 7 7 6326.866000 COPY_TO 30 7 8 6219.435000 COPY_TO 30 7 9 6229.165000 COPY_TO 30 7 10 6182.055000 COPY_FROM 30 7 1 4064.754000 COPY_FROM 30 7 2 4161.991000 COPY_FROM 30 7 3 4099.098000 COPY_FROM 30 7 4 4098.243000 COPY_FROM 30 7 5 4094.954000 COPY_FROM 30 7 6 4113.331000 COPY_FROM 30 7 7 4162.527000 COPY_FROM 30 7 8 4117.655000 COPY_FROM 30 7 9 4038.147000 COPY_FROM 30 7 10 4247.750000 COPY_TO 30 8 1 7036.335000 COPY_TO 30 8 2 7161.077000 COPY_TO 30 8 3 7198.475000 COPY_TO 30 8 4 7057.568000 COPY_TO 30 8 5 7068.777000 COPY_TO 30 8 6 7145.575000 COPY_TO 30 8 7 7164.393000 COPY_TO 30 8 8 7146.893000 COPY_TO 30 8 9 7263.004000 COPY_TO 30 8 10 7258.462000 COPY_FROM 30 8 1 4709.346000 COPY_FROM 30 8 2 4727.176000 COPY_FROM 30 8 3 4643.916000 COPY_FROM 30 8 4 4646.425000 COPY_FROM 30 8 5 4714.948000 COPY_FROM 30 8 6 4669.370000 COPY_FROM 30 8 7 4649.179000 COPY_FROM 30 8 8 4604.831000 COPY_FROM 30 8 9 4657.557000 COPY_FROM 30 8 10 4672.892000 COPY_TO 30 9 1 7908.138000 COPY_TO 30 9 2 8046.895000 COPY_TO 30 9 3 8140.333000 COPY_TO 30 9 4 8103.733000 COPY_TO 30 9 5 8007.650000 COPY_TO 30 9 6 7955.601000 COPY_TO 30 9 7 8044.544000 COPY_TO 30 9 8 8086.140000 COPY_TO 30 9 9 8062.369000 COPY_TO 30 9 10 7827.011000 COPY_FROM 30 9 1 5204.533000 COPY_FROM 30 9 2 5201.463000 COPY_FROM 30 9 3 5234.632000 COPY_FROM 30 9 4 5236.902000 COPY_FROM 30 9 5 5269.275000 COPY_FROM 30 9 6 5263.596000 COPY_FROM 30 9 7 5192.508000 COPY_FROM 30 9 8 5234.723000 COPY_FROM 30 9 9 5188.671000 COPY_FROM 30 9 10 5160.328000 COPY_TO 30 10 1 8859.946000 COPY_TO 30 10 2 8904.060000 COPY_TO 30 10 3 9075.677000 COPY_TO 30 10 4 8911.511000 COPY_TO 30 10 5 8923.505000 COPY_TO 30 10 6 8955.312000 COPY_TO 30 10 7 9014.532000 COPY_TO 30 10 8 9100.991000 COPY_TO 30 10 9 8978.536000 COPY_TO 30 10 10 8974.878000 COPY_FROM 30 10 1 5820.402000 COPY_FROM 30 10 2 5800.793000 COPY_FROM 30 10 3 5817.289000 COPY_FROM 30 10 4 5766.636000 COPY_FROM 30 10 5 5947.599000 COPY_FROM 30 10 6 5756.134000 COPY_FROM 30 10 7 5764.180000 COPY_FROM 30 10 8 5796.569000 COPY_FROM 30 10 9 5796.612000 COPY_FROM 30 10 10 5849.049000 COPY_TO 5 1 1 226.623000 COPY_TO 5 1 2 227.444000 COPY_TO 5 1 3 214.579000 COPY_TO 5 1 4 218.737000 COPY_TO 5 1 5 218.708000 COPY_TO 5 1 6 221.763000 COPY_TO 5 1 7 212.154000 COPY_TO 5 1 8 219.050000 COPY_TO 5 1 9 225.217000 COPY_TO 5 1 10 219.609000 COPY_FROM 5 1 1 190.806000 COPY_FROM 5 1 2 192.065000 COPY_FROM 5 1 3 192.423000 COPY_FROM 5 1 4 200.560000 COPY_FROM 5 1 5 190.027000 COPY_FROM 5 1 6 190.954000 COPY_FROM 5 1 7 190.775000 COPY_FROM 5 1 8 187.590000 COPY_FROM 5 1 9 194.545000 COPY_FROM 5 1 10 190.831000 COPY_TO 5 2 1 419.239000 COPY_TO 5 2 2 428.527000 COPY_TO 5 2 3 424.408000 COPY_TO 5 2 4 428.882000 COPY_TO 5 2 5 419.476000 COPY_TO 5 2 6 422.894000 COPY_TO 5 2 7 418.744000 COPY_TO 5 2 8 425.265000 COPY_TO 5 2 9 428.402000 COPY_TO 5 2 10 425.687000 COPY_FROM 5 2 1 368.581000 COPY_FROM 5 2 2 368.334000 COPY_FROM 5 2 3 379.807000 COPY_FROM 5 2 4 367.980000 COPY_FROM 5 2 5 364.015000 COPY_FROM 5 2 6 366.088000 COPY_FROM 5 2 7 360.102000 COPY_FROM 5 2 8 366.403000 COPY_FROM 5 2 9 363.637000 COPY_FROM 5 2 10 362.853000 COPY_TO 5 3 1 636.678000 COPY_TO 5 3 2 635.709000 COPY_TO 5 3 3 631.716000 COPY_TO 5 3 4 608.849000 COPY_TO 5 3 5 625.253000 COPY_TO 5 3 6 630.432000 COPY_TO 5 3 7 636.818000 COPY_TO 5 3 8 640.687000 COPY_TO 5 3 9 651.740000 COPY_TO 5 3 10 622.738000 COPY_FROM 5 3 1 541.713000 COPY_FROM 5 3 2 532.056000 COPY_FROM 5 3 3 539.630000 COPY_FROM 5 3 4 549.629000 COPY_FROM 5 3 5 548.109000 COPY_FROM 5 3 6 533.228000 COPY_FROM 5 3 7 532.981000 COPY_FROM 5 3 8 527.524000 COPY_FROM 5 3 9 566.548000 COPY_FROM 5 3 10 531.553000 COPY_TO 5 4 1 823.149000 COPY_TO 5 4 2 842.084000 COPY_TO 5 4 3 841.990000 COPY_TO 5 4 4 834.844000 COPY_TO 5 4 5 847.631000 COPY_TO 5 4 6 852.530000 COPY_TO 5 4 7 822.453000 COPY_TO 5 4 8 851.579000 COPY_TO 5 4 9 841.356000 COPY_TO 5 4 10 840.655000 COPY_FROM 5 4 1 715.727000 COPY_FROM 5 4 2 700.656000 COPY_FROM 5 4 3 714.135000 COPY_FROM 5 4 4 711.922000 COPY_FROM 5 4 5 703.007000 COPY_FROM 5 4 6 700.765000 COPY_FROM 5 4 7 705.071000 COPY_FROM 5 4 8 716.543000 COPY_FROM 5 4 9 702.448000 COPY_FROM 5 4 10 716.714000 COPY_TO 5 5 1 1044.045000 COPY_TO 5 5 2 1039.683000 COPY_TO 5 5 3 1010.508000 COPY_TO 5 5 4 1032.182000 COPY_TO 5 5 5 1056.995000 COPY_TO 5 5 6 1028.120000 COPY_TO 5 5 7 1035.610000 COPY_TO 5 5 8 1047.220000 COPY_TO 5 5 9 1056.572000 COPY_TO 5 5 10 1052.532000 COPY_FROM 5 5 1 880.451000 COPY_FROM 5 5 2 892.421000 COPY_FROM 5 5 3 926.924000 COPY_FROM 5 5 4 891.630000 COPY_FROM 5 5 5 931.319000 COPY_FROM 5 5 6 900.775000 COPY_FROM 5 5 7 894.377000 COPY_FROM 5 5 8 892.984000 COPY_FROM 5 5 9 882.452000 COPY_FROM 5 5 10 941.360000 COPY_TO 5 6 1 1258.759000 COPY_TO 5 6 2 1259.336000 COPY_TO 5 6 3 1268.761000 COPY_TO 5 6 4 1234.730000 COPY_TO 5 6 5 1272.013000 COPY_TO 5 6 6 1233.970000 COPY_TO 5 6 7 1281.098000 COPY_TO 5 6 8 1267.348000 COPY_TO 5 6 9 1259.674000 COPY_TO 5 6 10 1266.219000 COPY_FROM 5 6 1 1052.524000 COPY_FROM 5 6 2 1067.610000 COPY_FROM 5 6 3 1057.225000 COPY_FROM 5 6 4 1053.887000 COPY_FROM 5 6 5 1066.923000 COPY_FROM 5 6 6 1066.930000 COPY_FROM 5 6 7 1064.119000 COPY_FROM 5 6 8 1103.817000 COPY_FROM 5 6 9 1040.265000 COPY_FROM 5 6 10 1049.068000 COPY_TO 5 7 1 1492.215000 COPY_TO 5 7 2 1488.576000 COPY_TO 5 7 3 1467.710000 COPY_TO 5 7 4 1478.339000 COPY_TO 5 7 5 1501.272000 COPY_TO 5 7 6 1483.944000 COPY_TO 5 7 7 1479.922000 COPY_TO 5 7 8 1476.075000 COPY_TO 5 7 9 1470.403000 COPY_TO 5 7 10 1504.996000 COPY_FROM 5 7 1 1231.400000 COPY_FROM 5 7 2 1207.745000 COPY_FROM 5 7 3 1238.918000 COPY_FROM 5 7 4 1228.868000 COPY_FROM 5 7 5 1239.988000 COPY_FROM 5 7 6 1230.274000 COPY_FROM 5 7 7 1236.876000 COPY_FROM 5 7 8 1227.257000 COPY_FROM 5 7 9 1230.378000 COPY_FROM 5 7 10 1286.864000 COPY_TO 5 8 1 1739.946000 COPY_TO 5 8 2 1699.952000 COPY_TO 5 8 3 1679.076000 COPY_TO 5 8 4 1686.910000 COPY_TO 5 8 5 1688.083000 COPY_TO 5 8 6 1694.051000 COPY_TO 5 8 7 1678.831000 COPY_TO 5 8 8 1659.907000 COPY_TO 5 8 9 1641.518000 COPY_TO 5 8 10 1679.057000 COPY_FROM 5 8 1 1437.100000 COPY_FROM 5 8 2 1424.070000 COPY_FROM 5 8 3 1473.867000 COPY_FROM 5 8 4 1405.431000 COPY_FROM 5 8 5 1406.246000 COPY_FROM 5 8 6 1419.742000 COPY_FROM 5 8 7 1387.097000 COPY_FROM 5 8 8 1396.140000 COPY_FROM 5 8 9 1420.520000 COPY_FROM 5 8 10 1412.001000 COPY_TO 5 9 1 1858.925000 COPY_TO 5 9 2 1850.901000 COPY_TO 5 9 3 1873.408000 COPY_TO 5 9 4 1905.935000 COPY_TO 5 9 5 1910.295000 COPY_TO 5 9 6 1889.258000 COPY_TO 5 9 7 1865.899000 COPY_TO 5 9 8 1874.485000 COPY_TO 5 9 9 1906.459000 COPY_TO 5 9 10 1844.316000 COPY_FROM 5 9 1 1584.546000 COPY_FROM 5 9 2 1586.177000 COPY_FROM 5 9 3 1578.157000 COPY_FROM 5 9 4 1553.313000 COPY_FROM 5 9 5 1547.309000 COPY_FROM 5 9 6 1588.149000 COPY_FROM 5 9 7 1569.061000 COPY_FROM 5 9 8 1579.066000 COPY_FROM 5 9 9 1570.615000 COPY_FROM 5 9 10 1592.860000 COPY_TO 5 10 1 2095.472000 COPY_TO 5 10 2 2077.086000 COPY_TO 5 10 3 2082.011000 COPY_TO 5 10 4 2118.808000 COPY_TO 5 10 5 2122.738000 COPY_TO 5 10 6 2116.635000 COPY_TO 5 10 7 2065.169000 COPY_TO 5 10 8 2071.043000 COPY_TO 5 10 9 2104.322000 COPY_TO 5 10 10 2094.018000 COPY_FROM 5 10 1 1746.916000 COPY_FROM 5 10 2 1748.130000 COPY_FROM 5 10 3 1742.154000 COPY_FROM 5 10 4 1822.064000 COPY_FROM 5 10 5 1739.668000 COPY_FROM 5 10 6 1736.219000 COPY_FROM 5 10 7 1828.462000 COPY_FROM 5 10 8 1741.385000 COPY_FROM 5 10 9 1749.339000 COPY_FROM 5 10 10 1749.511000 COPY_TO 10 1 1 348.214000 COPY_TO 10 1 2 344.409000 COPY_TO 10 1 3 348.118000 COPY_TO 10 1 4 351.250000 COPY_TO 10 1 5 345.164000 COPY_TO 10 1 6 348.686000 COPY_TO 10 1 7 347.033000 COPY_TO 10 1 8 356.881000 COPY_TO 10 1 9 363.224000 COPY_TO 10 1 10 344.265000 COPY_FROM 10 1 1 309.644000 COPY_FROM 10 1 2 315.303000 COPY_FROM 10 1 3 309.133000 COPY_FROM 10 1 4 308.645000 COPY_FROM 10 1 5 307.657000 COPY_FROM 10 1 6 308.826000 COPY_FROM 10 1 7 306.510000 COPY_FROM 10 1 8 306.906000 COPY_FROM 10 1 9 312.354000 COPY_FROM 10 1 10 309.572000 COPY_TO 10 2 1 680.964000 COPY_TO 10 2 2 686.492000 COPY_TO 10 2 3 673.836000 COPY_TO 10 2 4 693.009000 COPY_TO 10 2 5 679.089000 COPY_TO 10 2 6 675.584000 COPY_TO 10 2 7 682.799000 COPY_TO 10 2 8 692.569000 COPY_TO 10 2 9 671.136000 COPY_TO 10 2 10 658.264000 COPY_FROM 10 2 1 596.359000 COPY_FROM 10 2 2 592.091000 COPY_FROM 10 2 3 593.862000 COPY_FROM 10 2 4 595.358000 COPY_FROM 10 2 5 595.974000 COPY_FROM 10 2 6 620.312000 COPY_FROM 10 2 7 596.066000 COPY_FROM 10 2 8 600.032000 COPY_FROM 10 2 9 600.454000 COPY_FROM 10 2 10 596.003000 COPY_TO 10 3 1 1019.610000 COPY_TO 10 3 2 1007.821000 COPY_TO 10 3 3 1014.551000 COPY_TO 10 3 4 1004.209000 COPY_TO 10 3 5 1037.550000 COPY_TO 10 3 6 1006.828000 COPY_TO 10 3 7 1018.162000 COPY_TO 10 3 8 992.985000 COPY_TO 10 3 9 1025.867000 COPY_TO 10 3 10 1028.286000 COPY_FROM 10 3 1 892.287000 COPY_FROM 10 3 2 887.084000 COPY_FROM 10 3 3 892.174000 COPY_FROM 10 3 4 899.172000 COPY_FROM 10 3 5 880.837000 COPY_FROM 10 3 6 885.155000 COPY_FROM 10 3 7 893.880000 COPY_FROM 10 3 8 870.693000 COPY_FROM 10 3 9 882.712000 COPY_FROM 10 3 10 878.129000 COPY_TO 10 4 1 1358.053000 COPY_TO 10 4 2 1360.787000 COPY_TO 10 4 3 1322.403000 COPY_TO 10 4 4 1388.729000 COPY_TO 10 4 5 1371.818000 COPY_TO 10 4 6 1349.647000 COPY_TO 10 4 7 1373.746000 COPY_TO 10 4 8 1426.870000 COPY_TO 10 4 9 1347.131000 COPY_TO 10 4 10 1336.103000 COPY_FROM 10 4 1 1187.230000 COPY_FROM 10 4 2 1175.994000 COPY_FROM 10 4 3 1186.190000 COPY_FROM 10 4 4 1186.168000 COPY_FROM 10 4 5 1182.343000 COPY_FROM 10 4 6 1179.842000 COPY_FROM 10 4 7 1175.598000 COPY_FROM 10 4 8 1189.885000 COPY_FROM 10 4 9 1166.391000 COPY_FROM 10 4 10 1175.445000 COPY_TO 10 5 1 1715.079000 COPY_TO 10 5 2 1685.962000 COPY_TO 10 5 3 1670.427000 COPY_TO 10 5 4 1662.272000 COPY_TO 10 5 5 1683.975000 COPY_TO 10 5 6 1662.505000 COPY_TO 10 5 7 1678.846000 COPY_TO 10 5 8 1659.495000 COPY_TO 10 5 9 1640.480000 COPY_TO 10 5 10 1666.186000 COPY_FROM 10 5 1 1469.628000 COPY_FROM 10 5 2 1482.367000 COPY_FROM 10 5 3 1467.941000 COPY_FROM 10 5 4 1443.202000 COPY_FROM 10 5 5 1438.004000 COPY_FROM 10 5 6 1435.447000 COPY_FROM 10 5 7 1440.746000 COPY_FROM 10 5 8 1440.996000 COPY_FROM 10 5 9 1516.147000 COPY_FROM 10 5 10 1523.081000 COPY_TO 10 6 1 2008.597000 COPY_TO 10 6 2 2002.489000 COPY_TO 10 6 3 2054.108000 COPY_TO 10 6 4 2020.325000 COPY_TO 10 6 5 2074.237000 COPY_TO 10 6 6 2029.803000 COPY_TO 10 6 7 2004.565000 COPY_TO 10 6 8 2027.488000 COPY_TO 10 6 9 2018.207000 COPY_TO 10 6 10 2043.407000 COPY_FROM 10 6 1 1732.059000 COPY_FROM 10 6 2 1698.740000 COPY_FROM 10 6 3 1744.397000 COPY_FROM 10 6 4 1752.396000 COPY_FROM 10 6 5 1738.473000 COPY_FROM 10 6 6 1750.763000 COPY_FROM 10 6 7 1760.260000 COPY_FROM 10 6 8 1734.506000 COPY_FROM 10 6 9 1752.343000 COPY_FROM 10 6 10 1800.136000 COPY_TO 10 7 1 2373.267000 COPY_TO 10 7 2 2359.455000 COPY_TO 10 7 3 2376.194000 COPY_TO 10 7 4 2369.713000 COPY_TO 10 7 5 2380.525000 COPY_TO 10 7 6 2355.680000 COPY_TO 10 7 7 2365.292000 COPY_TO 10 7 8 2387.594000 COPY_TO 10 7 9 2361.091000 COPY_TO 10 7 10 2399.029000 COPY_FROM 10 7 1 2040.625000 COPY_FROM 10 7 2 2031.799000 COPY_FROM 10 7 3 2054.883000 COPY_FROM 10 7 4 2020.964000 COPY_FROM 10 7 5 2085.711000 COPY_FROM 10 7 6 2056.172000 COPY_FROM 10 7 7 2053.141000 COPY_FROM 10 7 8 2017.080000 COPY_FROM 10 7 9 2036.249000 COPY_FROM 10 7 10 2055.574000 COPY_TO 10 8 1 2708.496000 COPY_TO 10 8 2 2670.277000 COPY_TO 10 8 3 2739.491000 COPY_TO 10 8 4 2670.203000 COPY_TO 10 8 5 2686.905000 COPY_TO 10 8 6 2715.423000 COPY_TO 10 8 7 2661.954000 COPY_TO 10 8 8 2679.533000 COPY_TO 10 8 9 2700.084000 COPY_TO 10 8 10 2692.732000 COPY_FROM 10 8 1 2332.678000 COPY_FROM 10 8 2 2327.148000 COPY_FROM 10 8 3 2365.272000 COPY_FROM 10 8 4 2323.775000 COPY_FROM 10 8 5 2327.727000 COPY_FROM 10 8 6 2328.340000 COPY_FROM 10 8 7 2351.656000 COPY_FROM 10 8 8 2359.587000 COPY_FROM 10 8 9 2315.807000 COPY_FROM 10 8 10 2323.951000 COPY_TO 10 9 1 3048.751000 COPY_TO 10 9 2 3047.431000 COPY_TO 10 9 3 3033.034000 COPY_TO 10 9 4 3024.685000 COPY_TO 10 9 5 3033.612000 COPY_TO 10 9 6 3071.925000 COPY_TO 10 9 7 3066.067000 COPY_TO 10 9 8 3061.065000 COPY_TO 10 9 9 3033.557000 COPY_TO 10 9 10 3139.233000 COPY_FROM 10 9 1 2637.134000 COPY_FROM 10 9 2 2648.296000 COPY_FROM 10 9 3 2595.698000 COPY_FROM 10 9 4 2684.115000 COPY_FROM 10 9 5 2640.266000 COPY_FROM 10 9 6 2647.282000 COPY_FROM 10 9 7 2626.573000 COPY_FROM 10 9 8 2597.198000 COPY_FROM 10 9 9 2590.305000 COPY_FROM 10 9 10 2607.834000 COPY_TO 10 10 1 3399.538000 COPY_TO 10 10 2 3395.112000 COPY_TO 10 10 3 3379.849000 COPY_TO 10 10 4 3447.512000 COPY_TO 10 10 5 3395.209000 COPY_TO 10 10 6 3372.455000 COPY_TO 10 10 7 3426.450000 COPY_TO 10 10 8 3406.147000 COPY_TO 10 10 9 3401.163000 COPY_TO 10 10 10 3398.863000 COPY_FROM 10 10 1 2918.524000 COPY_FROM 10 10 2 2946.519000 COPY_FROM 10 10 3 2897.459000 COPY_FROM 10 10 4 2949.553000 COPY_FROM 10 10 5 2924.340000 COPY_FROM 10 10 6 2880.430000 COPY_FROM 10 10 7 2943.481000 COPY_FROM 10 10 8 2924.866000 COPY_FROM 10 10 9 2882.415000 COPY_FROM 10 10 10 2939.448000 COPY_TO 15 1 1 481.490000 COPY_TO 15 1 2 480.802000 COPY_TO 15 1 3 505.153000 COPY_TO 15 1 4 480.755000 COPY_TO 15 1 5 487.445000 COPY_TO 15 1 6 478.630000 COPY_TO 15 1 7 471.924000 COPY_TO 15 1 8 484.494000 COPY_TO 15 1 9 475.958000 COPY_TO 15 1 10 476.259000 COPY_FROM 15 1 1 404.762000 COPY_FROM 15 1 2 411.539000 COPY_FROM 15 1 3 396.594000 COPY_FROM 15 1 4 402.033000 COPY_FROM 15 1 5 399.084000 COPY_FROM 15 1 6 402.425000 COPY_FROM 15 1 7 399.751000 COPY_FROM 15 1 8 396.732000 COPY_FROM 15 1 9 408.485000 COPY_FROM 15 1 10 401.768000 COPY_TO 15 2 1 948.346000 COPY_TO 15 2 2 960.359000 COPY_TO 15 2 3 945.425000 COPY_TO 15 2 4 942.055000 COPY_TO 15 2 5 946.342000 COPY_TO 15 2 6 974.876000 COPY_TO 15 2 7 935.041000 COPY_TO 15 2 8 962.795000 COPY_TO 15 2 9 934.524000 COPY_TO 15 2 10 944.476000 COPY_FROM 15 2 1 794.640000 COPY_FROM 15 2 2 764.601000 COPY_FROM 15 2 3 785.607000 COPY_FROM 15 2 4 768.691000 COPY_FROM 15 2 5 789.261000 COPY_FROM 15 2 6 766.484000 COPY_FROM 15 2 7 762.206000 COPY_FROM 15 2 8 777.008000 COPY_FROM 15 2 9 777.736000 COPY_FROM 15 2 10 768.562000 COPY_TO 15 3 1 1471.715000 COPY_TO 15 3 2 1425.784000 COPY_TO 15 3 3 1430.887000 COPY_TO 15 3 4 1411.350000 COPY_TO 15 3 5 1399.500000 COPY_TO 15 3 6 1414.848000 COPY_TO 15 3 7 1471.325000 COPY_TO 15 3 8 1424.225000 COPY_TO 15 3 9 1438.927000 COPY_TO 15 3 10 1383.432000 COPY_FROM 15 3 1 1157.842000 COPY_FROM 15 3 2 1148.168000 COPY_FROM 15 3 3 1170.290000 COPY_FROM 15 3 4 1163.281000 COPY_FROM 15 3 5 1164.792000 COPY_FROM 15 3 6 1170.901000 COPY_FROM 15 3 7 1167.411000 COPY_FROM 15 3 8 1136.925000 COPY_FROM 15 3 9 1163.268000 COPY_FROM 15 3 10 1167.786000 COPY_TO 15 4 1 1879.456000 COPY_TO 15 4 2 1851.491000 COPY_TO 15 4 3 1834.399000 COPY_TO 15 4 4 1909.106000 COPY_TO 15 4 5 1939.416000 COPY_TO 15 4 6 1856.175000 COPY_TO 15 4 7 1936.540000 COPY_TO 15 4 8 1872.650000 COPY_TO 15 4 9 1846.497000 COPY_TO 15 4 10 1851.336000 COPY_FROM 15 4 1 1527.390000 COPY_FROM 15 4 2 1559.869000 COPY_FROM 15 4 3 1549.983000 COPY_FROM 15 4 4 1519.352000 COPY_FROM 15 4 5 1534.720000 COPY_FROM 15 4 6 1531.672000 COPY_FROM 15 4 7 1514.365000 COPY_FROM 15 4 8 1524.385000 COPY_FROM 15 4 9 1519.783000 COPY_FROM 15 4 10 1518.088000 COPY_TO 15 5 1 2315.126000 COPY_TO 15 5 2 2403.590000 COPY_TO 15 5 3 2353.186000 COPY_TO 15 5 4 2362.140000 COPY_TO 15 5 5 2337.372000 COPY_TO 15 5 6 2369.436000 COPY_TO 15 5 7 2344.194000 COPY_TO 15 5 8 2345.627000 COPY_TO 15 5 9 2393.136000 COPY_TO 15 5 10 2390.355000 COPY_FROM 15 5 1 1904.628000 COPY_FROM 15 5 2 1910.340000 COPY_FROM 15 5 3 1918.427000 COPY_FROM 15 5 4 1912.737000 COPY_FROM 15 5 5 1955.806000 COPY_FROM 15 5 6 1892.326000 COPY_FROM 15 5 7 1915.079000 COPY_FROM 15 5 8 1920.116000 COPY_FROM 15 5 9 1914.245000 COPY_FROM 15 5 10 1887.371000 COPY_TO 15 6 1 2825.865000 COPY_TO 15 6 2 2834.549000 COPY_TO 15 6 3 2838.698000 COPY_TO 15 6 4 2769.660000 COPY_TO 15 6 5 2771.549000 COPY_TO 15 6 6 2824.433000 COPY_TO 15 6 7 2850.494000 COPY_TO 15 6 8 2873.406000 COPY_TO 15 6 9 2819.338000 COPY_TO 15 6 10 2800.095000 COPY_FROM 15 6 1 2312.919000 COPY_FROM 15 6 2 2280.861000 COPY_FROM 15 6 3 2276.382000 COPY_FROM 15 6 4 2328.440000 COPY_FROM 15 6 5 2306.146000 COPY_FROM 15 6 6 2290.642000 COPY_FROM 15 6 7 2318.425000 COPY_FROM 15 6 8 2319.431000 COPY_FROM 15 6 9 2271.906000 COPY_FROM 15 6 10 2307.933000 COPY_TO 15 7 1 3276.071000 COPY_TO 15 7 2 3302.277000 COPY_TO 15 7 3 3227.547000 COPY_TO 15 7 4 3205.014000 COPY_TO 15 7 5 3216.083000 COPY_TO 15 7 6 3288.328000 COPY_TO 15 7 7 3273.990000 COPY_TO 15 7 8 3269.459000 COPY_TO 15 7 9 3276.029000 COPY_TO 15 7 10 3257.944000 COPY_FROM 15 7 1 2650.110000 COPY_FROM 15 7 2 2687.294000 COPY_FROM 15 7 3 2679.115000 COPY_FROM 15 7 4 2642.092000 COPY_FROM 15 7 5 2754.050000 COPY_FROM 15 7 6 2670.190000 COPY_FROM 15 7 7 2659.509000 COPY_FROM 15 7 8 2680.944000 COPY_FROM 15 7 9 2702.110000 COPY_FROM 15 7 10 2714.737000 COPY_TO 15 8 1 3743.204000 COPY_TO 15 8 2 3728.396000 COPY_TO 15 8 3 3694.741000 COPY_TO 15 8 4 3792.445000 COPY_TO 15 8 5 3774.482000 COPY_TO 15 8 6 3741.767000 COPY_TO 15 8 7 3763.394000 COPY_TO 15 8 8 3760.802000 COPY_TO 15 8 9 3778.522000 COPY_TO 15 8 10 3719.006000 COPY_FROM 15 8 1 3036.831000 COPY_FROM 15 8 2 3053.770000 COPY_FROM 15 8 3 3084.446000 COPY_FROM 15 8 4 3083.344000 COPY_FROM 15 8 5 3063.590000 COPY_FROM 15 8 6 2994.047000 COPY_FROM 15 8 7 2997.051000 COPY_FROM 15 8 8 3028.378000 COPY_FROM 15 8 9 2985.766000 COPY_FROM 15 8 10 3048.673000 COPY_TO 15 9 1 4209.894000 COPY_TO 15 9 2 4166.767000 COPY_TO 15 9 3 4381.260000 COPY_TO 15 9 4 4166.933000 COPY_TO 15 9 5 4208.765000 COPY_TO 15 9 6 4221.622000 COPY_TO 15 9 7 4243.140000 COPY_TO 15 9 8 4221.371000 COPY_TO 15 9 9 4206.701000 COPY_TO 15 9 10 4173.130000 COPY_FROM 15 9 1 3509.903000 COPY_FROM 15 9 2 3429.411000 COPY_FROM 15 9 3 3476.601000 COPY_FROM 15 9 4 3532.142000 COPY_FROM 15 9 5 3482.214000 COPY_FROM 15 9 6 3481.428000 COPY_FROM 15 9 7 3512.648000 COPY_FROM 15 9 8 3429.037000 COPY_FROM 15 9 9 3481.796000 COPY_FROM 15 9 10 3405.251000 COPY_TO 15 10 1 4783.435000 COPY_TO 15 10 2 4644.036000 COPY_TO 15 10 3 4681.145000 COPY_TO 15 10 4 4649.832000 COPY_TO 15 10 5 4695.900000 COPY_TO 15 10 6 4741.715000 COPY_TO 15 10 7 4645.073000 COPY_TO 15 10 8 4685.227000 COPY_TO 15 10 9 4667.401000 COPY_TO 15 10 10 4674.533000 COPY_FROM 15 10 1 3791.541000 COPY_FROM 15 10 2 3769.122000 COPY_FROM 15 10 3 3875.967000 COPY_FROM 15 10 4 3898.442000 COPY_FROM 15 10 5 3757.463000 COPY_FROM 15 10 6 3803.542000 COPY_FROM 15 10 7 3898.392000 COPY_FROM 15 10 8 3853.286000 COPY_FROM 15 10 9 3796.088000 COPY_FROM 15 10 10 3775.505000 COPY_TO 20 1 1 588.371000 COPY_TO 20 1 2 588.534000 COPY_TO 20 1 3 604.939000 COPY_TO 20 1 4 609.174000 COPY_TO 20 1 5 599.870000 COPY_TO 20 1 6 606.180000 COPY_TO 20 1 7 593.885000 COPY_TO 20 1 8 619.903000 COPY_TO 20 1 9 613.359000 COPY_TO 20 1 10 579.570000 COPY_FROM 20 1 1 512.783000 COPY_FROM 20 1 2 520.086000 COPY_FROM 20 1 3 526.413000 COPY_FROM 20 1 4 503.333000 COPY_FROM 20 1 5 499.622000 COPY_FROM 20 1 6 504.114000 COPY_FROM 20 1 7 522.611000 COPY_FROM 20 1 8 504.078000 COPY_FROM 20 1 9 519.839000 COPY_FROM 20 1 10 521.364000 COPY_TO 20 2 1 1192.504000 COPY_TO 20 2 2 1204.486000 COPY_TO 20 2 3 1184.451000 COPY_TO 20 2 4 1224.046000 COPY_TO 20 2 5 1168.863000 COPY_TO 20 2 6 1185.104000 COPY_TO 20 2 7 1213.106000 COPY_TO 20 2 8 1219.240000 COPY_TO 20 2 9 1239.428000 COPY_TO 20 2 10 1202.900000 COPY_FROM 20 2 1 970.190000 COPY_FROM 20 2 2 968.745000 COPY_FROM 20 2 3 968.316000 COPY_FROM 20 2 4 963.391000 COPY_FROM 20 2 5 977.050000 COPY_FROM 20 2 6 977.689000 COPY_FROM 20 2 7 986.514000 COPY_FROM 20 2 8 996.876000 COPY_FROM 20 2 9 988.527000 COPY_FROM 20 2 10 973.651000 COPY_TO 20 3 1 1830.128000 COPY_TO 20 3 2 1783.896000 COPY_TO 20 3 3 1824.977000 COPY_TO 20 3 4 1808.527000 COPY_TO 20 3 5 1831.361000 COPY_TO 20 3 6 1793.910000 COPY_TO 20 3 7 1790.156000 COPY_TO 20 3 8 1901.378000 COPY_TO 20 3 9 1808.887000 COPY_TO 20 3 10 1820.944000 COPY_FROM 20 3 1 1463.492000 COPY_FROM 20 3 2 1473.649000 COPY_FROM 20 3 3 1470.260000 COPY_FROM 20 3 4 1460.868000 COPY_FROM 20 3 5 1472.357000 COPY_FROM 20 3 6 1467.554000 COPY_FROM 20 3 7 1447.413000 COPY_FROM 20 3 8 1477.952000 COPY_FROM 20 3 9 1428.962000 COPY_FROM 20 3 10 1488.391000 COPY_TO 20 4 1 2425.563000 COPY_TO 20 4 2 2389.778000 COPY_TO 20 4 3 2423.539000 COPY_TO 20 4 4 2402.541000 COPY_TO 20 4 5 2435.064000 COPY_TO 20 4 6 2375.980000 COPY_TO 20 4 7 2405.196000 COPY_TO 20 4 8 2516.577000 COPY_TO 20 4 9 2420.785000 COPY_TO 20 4 10 2396.486000 COPY_FROM 20 4 1 1982.045000 COPY_FROM 20 4 2 1936.064000 COPY_FROM 20 4 3 1949.428000 COPY_FROM 20 4 4 1962.934000 COPY_FROM 20 4 5 1970.196000 COPY_FROM 20 4 6 2011.610000 COPY_FROM 20 4 7 1990.147000 COPY_FROM 20 4 8 1932.480000 COPY_FROM 20 4 9 1988.822000 COPY_FROM 20 4 10 1971.847000 COPY_TO 20 5 1 2981.284000 COPY_TO 20 5 2 2976.269000 COPY_TO 20 5 3 3098.910000 COPY_TO 20 5 4 2959.358000 COPY_TO 20 5 5 3077.661000 COPY_TO 20 5 6 2964.573000 COPY_TO 20 5 7 3000.287000 COPY_TO 20 5 8 3028.061000 COPY_TO 20 5 9 2981.529000 COPY_TO 20 5 10 2996.331000 COPY_FROM 20 5 1 2432.749000 COPY_FROM 20 5 2 2431.906000 COPY_FROM 20 5 3 2439.713000 COPY_FROM 20 5 4 2412.802000 COPY_FROM 20 5 5 2473.731000 COPY_FROM 20 5 6 2481.380000 COPY_FROM 20 5 7 2434.936000 COPY_FROM 20 5 8 2435.429000 COPY_FROM 20 5 9 2424.148000 COPY_FROM 20 5 10 2435.032000 COPY_TO 20 6 1 3515.496000 COPY_TO 20 6 2 3600.580000 COPY_TO 20 6 3 3661.207000 COPY_TO 20 6 4 3560.876000 COPY_TO 20 6 5 3560.392000 COPY_TO 20 6 6 3610.652000 COPY_TO 20 6 7 3592.674000 COPY_TO 20 6 8 3572.048000 COPY_TO 20 6 9 3552.225000 COPY_TO 20 6 10 3544.525000 COPY_FROM 20 6 1 3029.925000 COPY_FROM 20 6 2 2868.143000 COPY_FROM 20 6 3 3003.149000 COPY_FROM 20 6 4 3004.859000 COPY_FROM 20 6 5 2975.037000 COPY_FROM 20 6 6 2941.359000 COPY_FROM 20 6 7 2958.822000 COPY_FROM 20 6 8 2929.398000 COPY_FROM 20 6 9 2942.849000 COPY_FROM 20 6 10 2980.994000 COPY_TO 20 7 1 4116.153000 COPY_TO 20 7 2 4151.977000 COPY_TO 20 7 3 4191.156000 COPY_TO 20 7 4 4122.951000 COPY_TO 20 7 5 4132.277000 COPY_TO 20 7 6 4117.195000 COPY_TO 20 7 7 4102.429000 COPY_TO 20 7 8 4167.083000 COPY_TO 20 7 9 4186.748000 COPY_TO 20 7 10 4176.378000 COPY_FROM 20 7 1 3385.308000 COPY_FROM 20 7 2 3424.239000 COPY_FROM 20 7 3 3469.448000 COPY_FROM 20 7 4 3427.053000 COPY_FROM 20 7 5 3393.027000 COPY_FROM 20 7 6 3390.030000 COPY_FROM 20 7 7 3412.631000 COPY_FROM 20 7 8 3416.462000 COPY_FROM 20 7 9 3440.032000 COPY_FROM 20 7 10 3406.263000 COPY_TO 20 8 1 4763.503000 COPY_TO 20 8 2 4747.748000 COPY_TO 20 8 3 4701.871000 COPY_TO 20 8 4 4792.958000 COPY_TO 20 8 5 4802.671000 COPY_TO 20 8 6 4700.079000 COPY_TO 20 8 7 4735.823000 COPY_TO 20 8 8 4697.905000 COPY_TO 20 8 9 4792.274000 COPY_TO 20 8 10 4824.296000 COPY_FROM 20 8 1 3911.984000 COPY_FROM 20 8 2 3964.766000 COPY_FROM 20 8 3 3868.331000 COPY_FROM 20 8 4 3864.961000 COPY_FROM 20 8 5 3901.993000 COPY_FROM 20 8 6 3913.048000 COPY_FROM 20 8 7 3913.909000 COPY_FROM 20 8 8 3911.791000 COPY_FROM 20 8 9 3922.104000 COPY_FROM 20 8 10 3865.939000 COPY_TO 20 9 1 5345.060000 COPY_TO 20 9 2 5332.009000 COPY_TO 20 9 3 5396.687000 COPY_TO 20 9 4 5465.947000 COPY_TO 20 9 5 5338.527000 COPY_TO 20 9 6 5336.702000 COPY_TO 20 9 7 5312.314000 COPY_TO 20 9 8 5355.565000 COPY_TO 20 9 9 5299.061000 COPY_TO 20 9 10 5444.467000 COPY_FROM 20 9 1 4348.639000 COPY_FROM 20 9 2 4324.543000 COPY_FROM 20 9 3 4405.188000 COPY_FROM 20 9 4 4371.563000 COPY_FROM 20 9 5 4362.497000 COPY_FROM 20 9 6 4399.904000 COPY_FROM 20 9 7 4360.325000 COPY_FROM 20 9 8 4319.934000 COPY_FROM 20 9 9 4374.741000 COPY_FROM 20 9 10 4312.770000 COPY_TO 20 10 1 5898.919000 COPY_TO 20 10 2 5937.106000 COPY_TO 20 10 3 5982.895000 COPY_TO 20 10 4 6006.050000 COPY_TO 20 10 5 6017.783000 COPY_TO 20 10 6 6027.734000 COPY_TO 20 10 7 5980.353000 COPY_TO 20 10 8 5880.622000 COPY_TO 20 10 9 6004.364000 COPY_TO 20 10 10 5991.804000 COPY_FROM 20 10 1 4898.844000 COPY_FROM 20 10 2 4803.747000 COPY_FROM 20 10 3 4811.804000 COPY_FROM 20 10 4 4876.269000 COPY_FROM 20 10 5 4880.505000 COPY_FROM 20 10 6 4911.774000 COPY_FROM 20 10 7 4798.363000 COPY_FROM 20 10 8 4992.114000 COPY_FROM 20 10 9 4979.897000 COPY_FROM 20 10 10 4991.956000 COPY_TO 25 1 1 734.355000 COPY_TO 25 1 2 751.684000 COPY_TO 25 1 3 741.651000 COPY_TO 25 1 4 741.016000 COPY_TO 25 1 5 758.336000 COPY_TO 25 1 6 742.133000 COPY_TO 25 1 7 754.167000 COPY_TO 25 1 8 756.050000 COPY_TO 25 1 9 734.336000 COPY_TO 25 1 10 755.085000 COPY_FROM 25 1 1 596.991000 COPY_FROM 25 1 2 595.280000 COPY_FROM 25 1 3 592.821000 COPY_FROM 25 1 4 598.301000 COPY_FROM 25 1 5 592.075000 COPY_FROM 25 1 6 598.580000 COPY_FROM 25 1 7 612.489000 COPY_FROM 25 1 8 594.139000 COPY_FROM 25 1 9 606.733000 COPY_FROM 25 1 10 599.884000 COPY_TO 25 2 1 1454.990000 COPY_TO 25 2 2 1464.926000 COPY_TO 25 2 3 1442.608000 COPY_TO 25 2 4 1467.204000 COPY_TO 25 2 5 1469.535000 COPY_TO 25 2 6 1441.349000 COPY_TO 25 2 7 1465.709000 COPY_TO 25 2 8 1477.000000 COPY_TO 25 2 9 1466.388000 COPY_TO 25 2 10 1491.142000 COPY_FROM 25 2 1 1186.171000 COPY_FROM 25 2 2 1197.788000 COPY_FROM 25 2 3 1189.075000 COPY_FROM 25 2 4 1168.622000 COPY_FROM 25 2 5 1194.059000 COPY_FROM 25 2 6 1175.976000 COPY_FROM 25 2 7 1180.774000 COPY_FROM 25 2 8 1240.536000 COPY_FROM 25 2 9 1216.618000 COPY_FROM 25 2 10 1162.956000 COPY_TO 25 3 1 2124.055000 COPY_TO 25 3 2 2208.083000 COPY_TO 25 3 3 2171.897000 COPY_TO 25 3 4 2200.009000 COPY_TO 25 3 5 2157.130000 COPY_TO 25 3 6 2178.217000 COPY_TO 25 3 7 2170.618000 COPY_TO 25 3 8 2159.246000 COPY_TO 25 3 9 2180.852000 COPY_TO 25 3 10 2162.987000 COPY_FROM 25 3 1 1814.018000 COPY_FROM 25 3 2 1777.425000 COPY_FROM 25 3 3 1804.940000 COPY_FROM 25 3 4 1777.249000 COPY_FROM 25 3 5 1755.835000 COPY_FROM 25 3 6 1762.743000 COPY_FROM 25 3 7 1791.698000 COPY_FROM 25 3 8 1812.374000 COPY_FROM 25 3 9 1807.720000 COPY_FROM 25 3 10 1779.357000 COPY_TO 25 4 1 2880.647000 COPY_TO 25 4 2 2914.773000 COPY_TO 25 4 3 2935.268000 COPY_TO 25 4 4 2899.907000 COPY_TO 25 4 5 2973.967000 COPY_TO 25 4 6 2957.416000 COPY_TO 25 4 7 2988.516000 COPY_TO 25 4 8 2960.672000 COPY_TO 25 4 9 2995.815000 COPY_TO 25 4 10 2938.285000 COPY_FROM 25 4 1 2390.532000 COPY_FROM 25 4 2 2402.782000 COPY_FROM 25 4 3 2380.246000 COPY_FROM 25 4 4 2313.968000 COPY_FROM 25 4 5 2336.368000 COPY_FROM 25 4 6 2333.098000 COPY_FROM 25 4 7 2341.465000 COPY_FROM 25 4 8 2364.110000 COPY_FROM 25 4 9 2347.709000 COPY_FROM 25 4 10 2440.437000 COPY_TO 25 5 1 3615.292000 COPY_TO 25 5 2 3706.462000 COPY_TO 25 5 3 3629.459000 COPY_TO 25 5 4 3614.923000 COPY_TO 25 5 5 3646.021000 COPY_TO 25 5 6 3622.527000 COPY_TO 25 5 7 3614.309000 COPY_TO 25 5 8 3590.665000 COPY_TO 25 5 9 3570.947000 COPY_TO 25 5 10 3616.614000 COPY_FROM 25 5 1 3025.729000 COPY_FROM 25 5 2 2890.083000 COPY_FROM 25 5 3 2832.641000 COPY_FROM 25 5 4 2896.295000 COPY_FROM 25 5 5 2906.869000 COPY_FROM 25 5 6 2964.634000 COPY_FROM 25 5 7 2981.976000 COPY_FROM 25 5 8 2899.424000 COPY_FROM 25 5 9 2952.928000 COPY_FROM 25 5 10 2956.315000 COPY_TO 25 6 1 4335.410000 COPY_TO 25 6 2 4352.629000 COPY_TO 25 6 3 4328.721000 COPY_TO 25 6 4 4329.229000 COPY_TO 25 6 5 4326.110000 COPY_TO 25 6 6 4350.785000 COPY_TO 25 6 7 4422.414000 COPY_TO 25 6 8 4327.600000 COPY_TO 25 6 9 4399.015000 COPY_TO 25 6 10 4344.686000 COPY_FROM 25 6 1 3546.817000 COPY_FROM 25 6 2 3583.911000 COPY_FROM 25 6 3 3577.654000 COPY_FROM 25 6 4 3545.870000 COPY_FROM 25 6 5 3517.331000 COPY_FROM 25 6 6 3621.318000 COPY_FROM 25 6 7 3518.093000 COPY_FROM 25 6 8 3487.595000 COPY_FROM 25 6 9 3502.635000 COPY_FROM 25 6 10 3442.832000 COPY_TO 25 7 1 5127.114000 COPY_TO 25 7 2 5147.491000 COPY_TO 25 7 3 5030.220000 COPY_TO 25 7 4 5039.242000 COPY_TO 25 7 5 5024.293000 COPY_TO 25 7 6 5177.402000 COPY_TO 25 7 7 5091.543000 COPY_TO 25 7 8 5047.738000 COPY_TO 25 7 9 5032.130000 COPY_TO 25 7 10 5125.969000 COPY_FROM 25 7 1 4236.840000 COPY_FROM 25 7 2 4166.021000 COPY_FROM 25 7 3 4072.998000 COPY_FROM 25 7 4 4044.735000 COPY_FROM 25 7 5 4095.923000 COPY_FROM 25 7 6 4100.569000 COPY_FROM 25 7 7 4065.397000 COPY_FROM 25 7 8 4038.183000 COPY_FROM 25 7 9 4051.760000 COPY_FROM 25 7 10 4100.604000 COPY_TO 25 8 1 5755.047000 COPY_TO 25 8 2 5882.932000 COPY_TO 25 8 3 5711.378000 COPY_TO 25 8 4 5750.234000 COPY_TO 25 8 5 5813.714000 COPY_TO 25 8 6 5818.114000 COPY_TO 25 8 7 5869.900000 COPY_TO 25 8 8 5792.470000 COPY_TO 25 8 9 5842.988000 COPY_TO 25 8 10 5826.206000 COPY_FROM 25 8 1 4633.711000 COPY_FROM 25 8 2 4604.926000 COPY_FROM 25 8 3 4843.423000 COPY_FROM 25 8 4 4623.654000 COPY_FROM 25 8 5 4764.668000 COPY_FROM 25 8 6 4665.590000 COPY_FROM 25 8 7 4743.428000 COPY_FROM 25 8 8 4684.806000 COPY_FROM 25 8 9 4625.929000 COPY_FROM 25 8 10 4796.581000 COPY_TO 25 9 1 6537.881000 COPY_TO 25 9 2 6467.843000 COPY_TO 25 9 3 6485.727000 COPY_TO 25 9 4 6419.503000 COPY_TO 25 9 5 6547.430000 COPY_TO 25 9 6 6647.516000 COPY_TO 25 9 7 6545.266000 COPY_TO 25 9 8 6483.089000 COPY_TO 25 9 9 6488.061000 COPY_TO 25 9 10 6501.837000 COPY_FROM 25 9 1 5218.724000 COPY_FROM 25 9 2 5322.326000 COPY_FROM 25 9 3 5134.734000 COPY_FROM 25 9 4 5181.265000 COPY_FROM 25 9 5 5236.810000 COPY_FROM 25 9 6 5394.804000 COPY_FROM 25 9 7 5216.023000 COPY_FROM 25 9 8 5228.834000 COPY_FROM 25 9 9 5233.673000 COPY_FROM 25 9 10 5370.429000 COPY_TO 25 10 1 7187.403000 COPY_TO 25 10 2 7237.522000 COPY_TO 25 10 3 7215.015000 COPY_TO 25 10 4 7310.549000 COPY_TO 25 10 5 7204.966000 COPY_TO 25 10 6 7395.831000 COPY_TO 25 10 7 7235.840000 COPY_TO 25 10 8 7335.155000 COPY_TO 25 10 9 7320.366000 COPY_TO 25 10 10 7399.998000 COPY_FROM 25 10 1 5766.539000 COPY_FROM 25 10 2 5868.594000 COPY_FROM 25 10 3 5717.698000 COPY_FROM 25 10 4 5824.276000 COPY_FROM 25 10 5 5865.970000 COPY_FROM 25 10 6 5943.953000 COPY_FROM 25 10 7 5730.136000 COPY_FROM 25 10 8 5856.029000 COPY_FROM 25 10 9 5782.006000 COPY_FROM 25 10 10 5872.458000 COPY_TO 30 1 1 855.345000 COPY_TO 30 1 2 871.867000 COPY_TO 30 1 3 855.503000 COPY_TO 30 1 4 852.658000 COPY_TO 30 1 5 872.763000 COPY_TO 30 1 6 840.381000 COPY_TO 30 1 7 858.728000 COPY_TO 30 1 8 854.579000 COPY_TO 30 1 9 849.983000 COPY_TO 30 1 10 865.011000 COPY_FROM 30 1 1 722.491000 COPY_FROM 30 1 2 697.416000 COPY_FROM 30 1 3 724.112000 COPY_FROM 30 1 4 723.154000 COPY_FROM 30 1 5 742.389000 COPY_FROM 30 1 6 736.040000 COPY_FROM 30 1 7 706.634000 COPY_FROM 30 1 8 703.661000 COPY_FROM 30 1 9 711.113000 COPY_FROM 30 1 10 698.277000 COPY_TO 30 2 1 1724.559000 COPY_TO 30 2 2 1719.146000 COPY_TO 30 2 3 1713.379000 COPY_TO 30 2 4 1715.448000 COPY_TO 30 2 5 1731.645000 COPY_TO 30 2 6 1715.003000 COPY_TO 30 2 7 1694.419000 COPY_TO 30 2 8 1692.365000 COPY_TO 30 2 9 1734.901000 COPY_TO 30 2 10 1753.928000 COPY_FROM 30 2 1 1381.412000 COPY_FROM 30 2 2 1393.055000 COPY_FROM 30 2 3 1429.064000 COPY_FROM 30 2 4 1400.549000 COPY_FROM 30 2 5 1390.625000 COPY_FROM 30 2 6 1399.524000 COPY_FROM 30 2 7 1428.245000 COPY_FROM 30 2 8 1396.228000 COPY_FROM 30 2 9 1394.769000 COPY_FROM 30 2 10 1376.140000 COPY_TO 30 3 1 2549.615000 COPY_TO 30 3 2 2549.549000 COPY_TO 30 3 3 2554.699000 COPY_TO 30 3 4 2620.901000 COPY_TO 30 3 5 2542.416000 COPY_TO 30 3 6 2463.919000 COPY_TO 30 3 7 2514.404000 COPY_TO 30 3 8 2606.338000 COPY_TO 30 3 9 2549.300000 COPY_TO 30 3 10 2614.069000 COPY_FROM 30 3 1 2061.723000 COPY_FROM 30 3 2 2054.595000 COPY_FROM 30 3 3 2064.499000 COPY_FROM 30 3 4 2029.387000 COPY_FROM 30 3 5 2060.673000 COPY_FROM 30 3 6 2071.234000 COPY_FROM 30 3 7 2039.847000 COPY_FROM 30 3 8 2034.512000 COPY_FROM 30 3 9 2048.970000 COPY_FROM 30 3 10 2070.192000 COPY_TO 30 4 1 3439.779000 COPY_TO 30 4 2 3415.562000 COPY_TO 30 4 3 3472.690000 COPY_TO 30 4 4 3426.406000 COPY_TO 30 4 5 3417.655000 COPY_TO 30 4 6 3420.833000 COPY_TO 30 4 7 3380.506000 COPY_TO 30 4 8 3462.000000 COPY_TO 30 4 9 3402.428000 COPY_TO 30 4 10 3428.111000 COPY_FROM 30 4 1 2733.262000 COPY_FROM 30 4 2 2683.878000 COPY_FROM 30 4 3 2821.240000 COPY_FROM 30 4 4 2768.113000 COPY_FROM 30 4 5 2867.414000 COPY_FROM 30 4 6 2759.740000 COPY_FROM 30 4 7 2796.335000 COPY_FROM 30 4 8 2688.241000 COPY_FROM 30 4 9 2693.820000 COPY_FROM 30 4 10 2731.140000 COPY_TO 30 5 1 4242.226000 COPY_TO 30 5 2 4337.764000 COPY_TO 30 5 3 4201.378000 COPY_TO 30 5 4 4276.924000 COPY_TO 30 5 5 4195.586000 COPY_TO 30 5 6 4147.869000 COPY_TO 30 5 7 4262.615000 COPY_TO 30 5 8 4283.672000 COPY_TO 30 5 9 4316.076000 COPY_TO 30 5 10 4265.417000 COPY_FROM 30 5 1 3414.952000 COPY_FROM 30 5 2 3484.110000 COPY_FROM 30 5 3 3410.230000 COPY_FROM 30 5 4 3456.846000 COPY_FROM 30 5 5 3383.937000 COPY_FROM 30 5 6 3430.556000 COPY_FROM 30 5 7 3430.628000 COPY_FROM 30 5 8 3428.378000 COPY_FROM 30 5 9 3396.417000 COPY_FROM 30 5 10 3432.408000 COPY_TO 30 6 1 5074.778000 COPY_TO 30 6 2 5101.994000 COPY_TO 30 6 3 5069.600000 COPY_TO 30 6 4 5222.574000 COPY_TO 30 6 5 5071.946000 COPY_TO 30 6 6 5076.127000 COPY_TO 30 6 7 5080.155000 COPY_TO 30 6 8 5189.124000 COPY_TO 30 6 9 5172.174000 COPY_TO 30 6 10 5100.780000 COPY_FROM 30 6 1 4211.710000 COPY_FROM 30 6 2 4088.827000 COPY_FROM 30 6 3 4140.018000 COPY_FROM 30 6 4 4200.005000 COPY_FROM 30 6 5 4083.156000 COPY_FROM 30 6 6 4142.306000 COPY_FROM 30 6 7 4302.596000 COPY_FROM 30 6 8 4166.638000 COPY_FROM 30 6 9 4063.275000 COPY_FROM 30 6 10 3989.077000 COPY_TO 30 7 1 5985.682000 COPY_TO 30 7 2 5944.822000 COPY_TO 30 7 3 5909.677000 COPY_TO 30 7 4 5959.397000 COPY_TO 30 7 5 5973.909000 COPY_TO 30 7 6 5971.125000 COPY_TO 30 7 7 5970.800000 COPY_TO 30 7 8 5928.120000 COPY_TO 30 7 9 6065.392000 COPY_TO 30 7 10 5967.311000 COPY_FROM 30 7 1 4832.597000 COPY_FROM 30 7 2 4763.587000 COPY_FROM 30 7 3 5007.212000 COPY_FROM 30 7 4 4831.589000 COPY_FROM 30 7 5 4761.464000 COPY_FROM 30 7 6 4964.790000 COPY_FROM 30 7 7 4911.089000 COPY_FROM 30 7 8 4804.915000 COPY_FROM 30 7 9 4830.199000 COPY_FROM 30 7 10 4821.159000 COPY_TO 30 8 1 6780.338000 COPY_TO 30 8 2 6780.465000 COPY_TO 30 8 3 6891.504000 COPY_TO 30 8 4 6924.545000 COPY_TO 30 8 5 6887.753000 COPY_TO 30 8 6 6667.140000 COPY_TO 30 8 7 6766.440000 COPY_TO 30 8 8 6847.607000 COPY_TO 30 8 9 6949.330000 COPY_TO 30 8 10 6807.099000 COPY_FROM 30 8 1 5408.566000 COPY_FROM 30 8 2 5430.909000 COPY_FROM 30 8 3 5413.220000 COPY_FROM 30 8 4 5426.873000 COPY_FROM 30 8 5 5471.004000 COPY_FROM 30 8 6 5454.879000 COPY_FROM 30 8 7 5467.374000 COPY_FROM 30 8 8 5463.669000 COPY_FROM 30 8 9 5382.302000 COPY_FROM 30 8 10 5430.827000 COPY_TO 30 9 1 7646.096000 COPY_TO 30 9 2 7663.106000 COPY_TO 30 9 3 7649.568000 COPY_TO 30 9 4 7582.509000 COPY_TO 30 9 5 7677.910000 COPY_TO 30 9 6 7649.933000 COPY_TO 30 9 7 7639.381000 COPY_TO 30 9 8 7628.082000 COPY_TO 30 9 9 7742.443000 COPY_TO 30 9 10 7749.198000 COPY_FROM 30 9 1 6254.021000 COPY_FROM 30 9 2 6189.310000 COPY_FROM 30 9 3 6080.114000 COPY_FROM 30 9 4 6117.857000 COPY_FROM 30 9 5 6120.318000 COPY_FROM 30 9 6 6131.465000 COPY_FROM 30 9 7 6119.603000 COPY_FROM 30 9 8 6132.356000 COPY_FROM 30 9 9 6217.884000 COPY_FROM 30 9 10 6169.986000 COPY_TO 30 10 1 8511.796000 COPY_TO 30 10 2 8541.021000 COPY_TO 30 10 3 8470.991000 COPY_TO 30 10 4 8429.901000 COPY_TO 30 10 5 8399.581000 COPY_TO 30 10 6 8449.127000 COPY_TO 30 10 7 8421.535000 COPY_TO 30 10 8 8409.578000 COPY_TO 30 10 9 8588.901000 COPY_TO 30 10 10 8615.748000 COPY_FROM 30 10 1 6838.794000 COPY_FROM 30 10 2 6835.900000 COPY_FROM 30 10 3 6685.443000 COPY_FROM 30 10 4 6878.933000 COPY_FROM 30 10 5 6862.674000 COPY_FROM 30 10 6 6709.240000 COPY_FROM 30 10 7 6805.730000 COPY_FROM 30 10 8 6793.489000 COPY_FROM 30 10 9 6638.819000 COPY_FROM 30 10 10 6852.015000 TO 5 1 100.56% 218.376000 219.609000 FROM 5 1 113.33% 168.493000 190.954000 TO 5 2 100.92% 421.387000 425.265000 FROM 5 2 115.55% 317.101000 366.403000 TO 5 3 101.80% 624.457000 635.709000 FROM 5 3 115.15% 468.651000 539.630000 TO 5 4 99.53% 845.936000 841.990000 FROM 5 4 115.26% 617.653000 711.922000 TO 5 5 100.60% 1037.773000 1044.045000 FROM 5 5 116.46% 767.966000 894.377000 TO 5 6 100.93% 1254.507000 1266.219000 FROM 5 6 115.60% 920.494000 1064.119000 TO 5 7 100.67% 1474.119000 1483.944000 FROM 5 7 114.04% 1079.762000 1231.400000 TO 5 8 99.77% 1690.789000 1686.910000 FROM 5 8 114.03% 1245.100000 1419.742000 TO 5 9 100.40% 1866.939000 1874.485000 FROM 5 9 115.12% 1371.727000 1579.066000 TO 5 10 100.15% 2092.245000 2095.472000 FROM 5 10 115.91% 1508.160000 1748.130000 TO 10 1 98.62% 353.087000 348.214000 FROM 10 1 118.65% 260.551000 309.133000 TO 10 2 97.77% 696.468000 680.964000 FROM 10 2 117.55% 507.076000 596.066000 TO 10 3 98.57% 1034.388000 1019.610000 FROM 10 3 118.70% 747.307000 887.084000 TO 10 4 97.77% 1391.879000 1360.787000 FROM 10 4 119.64% 988.250000 1182.343000 TO 10 5 96.89% 1724.061000 1670.427000 FROM 10 5 119.92% 1224.098000 1467.941000 TO 10 6 98.43% 2059.930000 2027.488000 FROM 10 6 119.10% 1470.005000 1750.763000 TO 10 7 98.50% 2409.333000 2373.267000 FROM 10 7 119.12% 1723.536000 2053.141000 TO 10 8 97.51% 2761.445000 2692.732000 FROM 10 8 118.76% 1960.546000 2328.340000 TO 10 9 98.34% 3100.206000 3048.751000 FROM 10 9 119.07% 2214.820000 2637.134000 TO 10 10 98.70% 3444.291000 3399.538000 FROM 10 10 118.79% 2462.314000 2924.866000 TO 15 1 97.71% 492.082000 480.802000 FROM 15 1 115.59% 347.820000 402.033000 TO 15 2 98.20% 963.658000 946.342000 FROM 15 2 115.79% 671.073000 777.008000 TO 15 3 97.90% 1456.382000 1425.784000 FROM 15 3 115.27% 1010.479000 1164.792000 TO 15 4 96.85% 1933.560000 1872.650000 FROM 15 4 113.92% 1340.700000 1527.390000 TO 15 5 98.32% 2402.419000 2362.140000 FROM 15 5 115.48% 1657.594000 1914.245000 TO 15 6 97.39% 2901.545000 2825.865000 FROM 15 6 116.00% 1989.522000 2307.933000 TO 15 7 97.47% 3359.085000 3273.990000 FROM 15 7 116.48% 2301.570000 2680.944000 TO 15 8 97.82% 3844.652000 3760.802000 FROM 15 8 114.43% 2664.116000 3048.673000 TO 15 9 97.71% 4308.416000 4209.894000 FROM 15 9 116.96% 2976.833000 3481.796000 TO 15 10 96.91% 4830.319000 4681.145000 FROM 15 10 115.09% 3304.798000 3803.542000 TO 20 1 96.05% 629.828000 604.939000 FROM 20 1 118.50% 438.673000 519.839000 TO 20 2 98.35% 1224.716000 1204.486000 FROM 20 2 112.61% 867.634000 977.050000 TO 20 3 97.96% 1858.945000 1820.944000 FROM 20 3 115.08% 1277.634000 1470.260000 TO 20 4 99.05% 2444.051000 2420.785000 FROM 20 4 116.54% 1692.007000 1971.847000 TO 20 5 97.15% 3084.210000 2996.331000 FROM 20 5 115.35% 2110.909000 2435.032000 TO 20 6 96.52% 3700.704000 3572.048000 FROM 20 6 117.61% 2529.492000 2975.037000 TO 20 7 96.11% 4320.033000 4151.977000 FROM 20 7 116.20% 2940.254000 3416.462000 TO 20 8 97.94% 4863.534000 4763.503000 FROM 20 8 115.93% 3374.520000 3911.984000 TO 20 9 97.82% 5463.960000 5345.060000 FROM 20 9 115.69% 3770.921000 4362.497000 TO 20 10 99.14% 6043.915000 5991.804000 FROM 20 10 116.88% 4191.494000 4898.844000 TO 25 1 98.29% 764.779000 751.684000 FROM 25 1 115.13% 519.686000 598.301000 TO 25 2 96.77% 1515.332000 1466.388000 FROM 25 2 116.70% 1018.943000 1189.075000 TO 25 3 94.74% 2292.456000 2171.897000 FROM 25 3 117.49% 1524.962000 1791.698000 TO 25 4 96.88% 3052.605000 2957.416000 FROM 25 4 117.70% 2008.544000 2364.110000 TO 25 5 94.08% 3843.996000 3616.614000 FROM 25 5 115.62% 2554.008000 2952.928000 TO 25 6 95.21% 4563.316000 4344.686000 FROM 25 6 118.56% 2990.859000 3545.870000 TO 25 7 95.55% 5328.781000 5091.543000 FROM 25 7 116.33% 3521.010000 4095.923000 TO 25 8 95.79% 6073.973000 5818.114000 FROM 25 8 116.83% 4009.777000 4684.806000 TO 25 9 95.80% 6787.185000 6501.837000 FROM 25 9 116.23% 4502.731000 5233.673000 TO 25 10 97.41% 7504.865000 7310.549000 FROM 25 10 117.25% 4994.463000 5856.029000 TO 30 1 94.39% 906.324000 855.503000 FROM 30 1 119.60% 604.110000 722.491000 TO 30 2 95.56% 1799.114000 1719.146000 FROM 30 2 117.45% 1188.794000 1396.228000 TO 30 3 95.76% 2662.493000 2549.615000 FROM 30 3 117.43% 1754.809000 2060.673000 TO 30 4 96.52% 3549.913000 3426.406000 FROM 30 4 117.23% 2354.055000 2759.740000 TO 30 5 96.50% 4419.907000 4265.417000 FROM 30 5 116.97% 2932.883000 3430.556000 TO 30 6 94.76% 5382.615000 5100.780000 FROM 30 6 117.88% 3514.108000 4142.306000 TO 30 7 95.52% 6250.630000 5970.800000 FROM 30 7 117.46% 4113.331000 4831.589000 TO 30 8 95.62% 7161.077000 6847.607000 FROM 30 8 116.31% 4669.370000 5430.909000 TO 30 9 95.07% 8046.895000 7649.933000 FROM 30 9 117.15% 5234.632000 6132.356000 TO 30 10 94.39% 8974.878000 8470.991000 FROM 30 10 117.84% 5800.793000 6835.900000 #!/usr/bin/env ruby from = File.open(ARGV[0]) to = File.open(ARGV[1]) loop do from_line = from.gets to_line = to.gets break if from_line.nil? break if to_line.nil? from_type, from_n_columns, from_n_rows, from_runtime = from_line.split to_type, to_n_columns, to_n_rows, to_runtime = to_line.split break if from_type != to_type break if from_n_columns != to_n_columns break if from_n_rows != to_n_rows runtime_diff = to_runtime.to_f/ from_runtime.to_f puts("%s\t%s\t%s\t%5.2f%%\t%s\t%s" % [ from_type[5..-1], from_n_columns, from_n_rows, runtime_diff * 100, from_runtime, to_runtime, ]) end #!/usr/bin/env ruby statistics = {} ARGF.each_line do |line| type, n_columns, n_rows, round, runtime = line.split statistics[[type, n_columns, n_rows]] ||= [] statistics[[type, n_columns, n_rows]] << runtime end require "pp" statistics.each do |(type, n_columns, n_rows), runtimes| runtime_median = runtimes.sort[runtimes.size / 2] puts("#{type}\t#{n_columns}\t#{n_rows}\t#{runtime_median}") end From 15505e1e2ed59644a567e40a7e8987823b49978d Mon Sep 17 00:00:00 2001 From: Sutou Kouhei <kou@clear-code.com> Date: Mon, 4 Mar 2024 13:52:34 +0900 Subject: [PATCH v19 1/5] Add CopyFromRoutine/CopyToRountine They are for implementing custom COPY FROM/TO format. But this is not enough to implement custom COPY FROM/TO format yet. We'll export some APIs to receive/send data and add "format" option to COPY FROM/TO later. Existing text/csv/binary format implementations don't use CopyFromRoutine/CopyToRoutine for now. We have a patch for it but we defer it. Because there are some mysterious profile results in spite of we get faster runtimes. See [1] for details. [1] https://www.postgresql.org/message-id/ZdbtQJ-p5H1_EDwE%40paquier.xyz Note that this doesn't change existing text/csv/binary format implementations. --- src/backend/commands/copyfrom.c | 24 +++++- src/backend/commands/copyfromparse.c | 5 ++ src/backend/commands/copyto.c | 31 ++++++- src/include/commands/copyapi.h | 101 +++++++++++++++++++++++ src/include/commands/copyfrom_internal.h | 4 + src/tools/pgindent/typedefs.list | 2 + 6 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 src/include/commands/copyapi.h diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index ce4d62e707c..ff13b3e3592 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -1618,12 +1618,22 @@ BeginCopyFrom(ParseState *pstate, /* Fetch the input function and typioparam info */ if (cstate->opts.binary) + { getTypeBinaryInputInfo(att->atttypid, &in_func_oid, &typioparams[attnum - 1]); + fmgr_info(in_func_oid, &in_functions[attnum - 1]); + } + else if (cstate->routine) + cstate->routine->CopyFromInFunc(cstate, att->atttypid, + &in_functions[attnum - 1], + &typioparams[attnum - 1]); + else + { getTypeInputInfo(att->atttypid, &in_func_oid, &typioparams[attnum - 1]); - fmgr_info(in_func_oid, &in_functions[attnum - 1]); + fmgr_info(in_func_oid, &in_functions[attnum - 1]); + } /* Get default info if available */ defexprs[attnum - 1] = NULL; @@ -1763,10 +1773,13 @@ BeginCopyFrom(ParseState *pstate, /* Read and verify binary header */ ReceiveCopyBinaryHeader(cstate); } - - /* create workspace for CopyReadAttributes results */ - if (!cstate->opts.binary) + else if (cstate->routine) { + cstate->routine->CopyFromStart(cstate, tupDesc); + } + else + { + /* create workspace for CopyReadAttributes results */ AttrNumber attr_count = list_length(cstate->attnumlist); cstate->max_fields = attr_count; @@ -1784,6 +1797,9 @@ BeginCopyFrom(ParseState *pstate, void EndCopyFrom(CopyFromState cstate) { + if (cstate->routine) + cstate->routine->CopyFromEnd(cstate); + /* No COPY FROM related resources except memory. */ if (cstate->is_program) { diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c index 7efcb891598..92b8d5e72d5 100644 --- a/src/backend/commands/copyfromparse.c +++ b/src/backend/commands/copyfromparse.c @@ -1012,6 +1012,11 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext, Assert(fieldno == attr_count); } + else if (cstate->routine) + { + if (!cstate->routine->CopyFromOneRow(cstate, econtext, values, nulls)) + return false; + } else { /* binary */ diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index ae8b2e36d72..ff19c457abf 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -20,6 +20,7 @@ #include "access/tableam.h" #include "commands/copy.h" +#include "commands/copyapi.h" #include "commands/progress.h" #include "executor/execdesc.h" #include "executor/executor.h" @@ -64,6 +65,9 @@ typedef enum CopyDest */ typedef struct CopyToStateData { + /* format routine */ + const CopyToRoutine *routine; + /* low-level state data */ CopyDest copy_dest; /* type of copy source/destination */ FILE *copy_file; /* used if copy_dest == COPY_FILE */ @@ -771,14 +775,22 @@ DoCopyTo(CopyToState cstate) Form_pg_attribute attr = TupleDescAttr(tupDesc, attnum - 1); if (cstate->opts.binary) + { getTypeBinaryOutputInfo(attr->atttypid, &out_func_oid, &isvarlena); + fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); + } + else if (cstate->routine) + cstate->routine->CopyToOutFunc(cstate, attr->atttypid, + &cstate->out_functions[attnum - 1]); else + { getTypeOutputInfo(attr->atttypid, &out_func_oid, &isvarlena); - fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); + fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); + } } /* @@ -805,6 +817,8 @@ DoCopyTo(CopyToState cstate) tmp = 0; CopySendInt32(cstate, tmp); } + else if (cstate->routine) + cstate->routine->CopyToStart(cstate, tupDesc); else { /* @@ -886,6 +900,8 @@ DoCopyTo(CopyToState cstate) /* Need to flush out the trailer */ CopySendEndOfRow(cstate); } + else if (cstate->routine) + cstate->routine->CopyToEnd(cstate); MemoryContextDelete(cstate->rowcontext); @@ -910,15 +926,22 @@ CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot) MemoryContextReset(cstate->rowcontext); oldcontext = MemoryContextSwitchTo(cstate->rowcontext); + /* Make sure the tuple is fully deconstructed */ + slot_getallattrs(slot); + + if (cstate->routine) + { + cstate->routine->CopyToOneRow(cstate, slot); + MemoryContextSwitchTo(oldcontext); + return; + } + if (cstate->opts.binary) { /* Binary per-tuple header */ CopySendInt16(cstate, list_length(cstate->attnumlist)); } - /* Make sure the tuple is fully deconstructed */ - slot_getallattrs(slot); - foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); diff --git a/src/include/commands/copyapi.h b/src/include/commands/copyapi.h new file mode 100644 index 00000000000..d1289424c67 --- /dev/null +++ b/src/include/commands/copyapi.h @@ -0,0 +1,101 @@ +/*------------------------------------------------------------------------- + * + * copyapi.h + * API for COPY TO/FROM handlers + * + * + * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/commands/copyapi.h + * + *------------------------------------------------------------------------- + */ +#ifndef COPYAPI_H +#define COPYAPI_H + +#include "executor/tuptable.h" +#include "nodes/execnodes.h" + +/* These are private in commands/copy[from|to].c */ +typedef struct CopyFromStateData *CopyFromState; +typedef struct CopyToStateData *CopyToState; + +/* + * API structure for a COPY FROM format implementation. Note this must be + * allocated in a server-lifetime manner, typically as a static const struct. + */ +typedef struct CopyFromRoutine +{ + /* + * Called when COPY FROM is started to set up the input functions + * associated with the relation's attributes writing to. `finfo` can be + * optionally filled to provide the catalog information of the input + * function. `typioparam` can be optionally filled to define the OID of + * the type to pass to the input function. `atttypid` is the OID of data + * type used by the relation's attribute. + */ + void (*CopyFromInFunc) (CopyFromState cstate, Oid atttypid, + FmgrInfo *finfo, Oid *typioparam); + + /* + * Called when COPY FROM is started. + * + * `tupDesc` is the tuple descriptor of the relation where the data needs + * to be copied. This can be used for any initialization steps required + * by a format. + */ + void (*CopyFromStart) (CopyFromState cstate, TupleDesc tupDesc); + + /* + * Copy one row to a set of `values` and `nulls` of size tupDesc->natts. + * + * 'econtext' is used to evaluate default expression for each column that + * is either not read from the file or is using the DEFAULT option of COPY + * FROM. It is NULL if no default values are used. + * + * Returns false if there are no more tuples to copy. + */ + bool (*CopyFromOneRow) (CopyFromState cstate, ExprContext *econtext, + Datum *values, bool *nulls); + + /* Called when COPY FROM has ended. */ + void (*CopyFromEnd) (CopyFromState cstate); +} CopyFromRoutine; + +/* + * API structure for a COPY TO format implementation. Note this must be + * allocated in a server-lifetime manner, typically as a static const struct. + */ +typedef struct CopyToRoutine +{ + /* + * Called when COPY TO is started to set up the output functions + * associated with the relation's attributes reading from. `finfo` can be + * optionally filled to provide the catalog information of the output + * function. `atttypid` is the OID of data type used by the relation's + * attribute. + */ + void (*CopyToOutFunc) (CopyToState cstate, Oid atttypid, + FmgrInfo *finfo); + + /* + * Called when COPY TO is started. + * + * `tupDesc` is the tuple descriptor of the relation from where the data + * is read. + */ + void (*CopyToStart) (CopyToState cstate, TupleDesc tupDesc); + + /* + * Copy one row for COPY TO. + * + * `slot` is the tuple slot where the data is emitted. + */ + void (*CopyToOneRow) (CopyToState cstate, TupleTableSlot *slot); + + /* Called when COPY TO has ended */ + void (*CopyToEnd) (CopyToState cstate); +} CopyToRoutine; + +#endif /* COPYAPI_H */ diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index cad52fcc783..509b9e92a18 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -15,6 +15,7 @@ #define COPYFROM_INTERNAL_H #include "commands/copy.h" +#include "commands/copyapi.h" #include "commands/trigger.h" #include "nodes/miscnodes.h" @@ -58,6 +59,9 @@ typedef enum CopyInsertMethod */ typedef struct CopyFromStateData { + /* format routine */ + const CopyFromRoutine *routine; + /* low-level state data */ CopySource copy_src; /* type of copy source */ FILE *copy_file; /* used if copy_src == COPY_FILE */ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d7f9217ce..3ce855c8f17 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -490,6 +490,7 @@ ConvertRowtypeExpr CookedConstraint CopyDest CopyFormatOptions +CopyFromRoutine CopyFromState CopyFromStateData CopyHeaderChoice @@ -501,6 +502,7 @@ CopyMultiInsertInfo CopyOnErrorChoice CopySource CopyStmt +CopyToRoutine CopyToState CopyToStateData Cost -- 2.45.2 From b26d39f09dc78b3338aa4527d43cd0468f5ddf69 Mon Sep 17 00:00:00 2001 From: Sutou Kouhei <kou@clear-code.com> Date: Tue, 23 Jul 2024 16:44:44 +0900 Subject: [PATCH v19 2/5] Use CopyFromRoutine/CopyToRountine for the existing formats The existing formats are text, csv and binary. If we find any performance regression by this, we will not merge this to master. This will increase indirect function call costs but this will reduce runtime "if (cstate->opts.binary)" and "if (cstate->opts.csv_mode)" branch costs. This uses an optimization based of static inline function and a constant argument call for cstate->opts.csv_mode. For example, CopyFromTextLikeOneRow() uses this optimization. It accepts the "bool is_csv" argument instead of using cstate->opts.csv_mode in it. CopyFromTextOneRow() calls CopyFromTextLikeOneRow() with false (constant) for "bool is_csv". Compiler will remove "if (is_csv)" branch in it by this optimization. This doesn't change existing logic. This just moves existing codes. --- src/backend/commands/copyfrom.c | 215 ++++++--- src/backend/commands/copyfromparse.c | 556 +++++++++++++---------- src/backend/commands/copyto.c | 480 ++++++++++++------- src/include/commands/copy.h | 2 - src/include/commands/copyfrom_internal.h | 8 + 5 files changed, 813 insertions(+), 448 deletions(-) diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index ff13b3e3592..1a59202f5ab 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -103,6 +103,157 @@ typedef struct CopyMultiInsertInfo /* non-export function prototypes */ static void ClosePipeFromProgram(CopyFromState cstate); + +/* + * CopyFromRoutine implementations for text and CSV. + */ + +/* + * CopyFromTextLikeInFunc + * + * Assign input function data for a relation's attribute in text/CSV format. + */ +static void +CopyFromTextLikeInFunc(CopyFromState cstate, Oid atttypid, + FmgrInfo *finfo, Oid *typioparam) +{ + Oid func_oid; + + getTypeInputInfo(atttypid, &func_oid, typioparam); + fmgr_info(func_oid, finfo); +} + +/* + * CopyFromTextLikeStart + * + * Start of COPY FROM for text/CSV format. + */ +static void +CopyFromTextLikeStart(CopyFromState cstate, TupleDesc tupDesc) +{ + AttrNumber attr_count; + + /* + * If encoding conversion is needed, we need another buffer to hold the + * converted input data. Otherwise, we can just point input_buf to the + * same buffer as raw_buf. + */ + if (cstate->need_transcoding) + { + cstate->input_buf = (char *) palloc(INPUT_BUF_SIZE + 1); + cstate->input_buf_index = cstate->input_buf_len = 0; + } + else + cstate->input_buf = cstate->raw_buf; + cstate->input_reached_eof = false; + + initStringInfo(&cstate->line_buf); + + /* + * Create workspace for CopyReadAttributes results; used by CSV and text + * format. + */ + attr_count = list_length(cstate->attnumlist); + cstate->max_fields = attr_count; + cstate->raw_fields = (char **) palloc(attr_count * sizeof(char *)); +} + +/* + * CopyFromTextLikeEnd + * + * End of COPY FROM for text/CSV format. + */ +static void +CopyFromTextLikeEnd(CopyFromState cstate) +{ + /* nothing to do */ +} + +/* + * CopyFromRoutine implementation for "binary". + */ + +/* + * CopyFromBinaryInFunc + * + * Assign input function data for a relation's attribute in binary format. + */ +static void +CopyFromBinaryInFunc(CopyFromState cstate, Oid atttypid, + FmgrInfo *finfo, Oid *typioparam) +{ + Oid func_oid; + + getTypeBinaryInputInfo(atttypid, &func_oid, typioparam); + fmgr_info(func_oid, finfo); +} + +/* + * CopyFromBinaryStart + * + * Start of COPY FROM for binary format. + */ +static void +CopyFromBinaryStart(CopyFromState cstate, TupleDesc tupDesc) +{ + /* Read and verify binary header */ + ReceiveCopyBinaryHeader(cstate); +} + +/* + * CopyFromBinaryEnd + * + * End of COPY FROM for binary format. + */ +static void +CopyFromBinaryEnd(CopyFromState cstate) +{ + /* nothing to do */ +} + +/* + * Routines assigned to each format. ++ + * CSV and text share the same implementation, at the exception of the + * per-row callback. + */ +static const CopyFromRoutine CopyFromRoutineText = { + .CopyFromInFunc = CopyFromTextLikeInFunc, + .CopyFromStart = CopyFromTextLikeStart, + .CopyFromOneRow = CopyFromTextOneRow, + .CopyFromEnd = CopyFromTextLikeEnd, +}; + +static const CopyFromRoutine CopyFromRoutineCSV = { + .CopyFromInFunc = CopyFromTextLikeInFunc, + .CopyFromStart = CopyFromTextLikeStart, + .CopyFromOneRow = CopyFromCSVOneRow, + .CopyFromEnd = CopyFromTextLikeEnd, +}; + +static const CopyFromRoutine CopyFromRoutineBinary = { + .CopyFromInFunc = CopyFromBinaryInFunc, + .CopyFromStart = CopyFromBinaryStart, + .CopyFromOneRow = CopyFromBinaryOneRow, + .CopyFromEnd = CopyFromBinaryEnd, +}; + +/* + * Define the COPY FROM routines to use for a format. + */ +static const CopyFromRoutine * +CopyFromGetRoutine(CopyFormatOptions opts) +{ + if (opts.csv_mode) + return &CopyFromRoutineCSV; + else if (opts.binary) + return &CopyFromRoutineBinary; + + /* default is text */ + return &CopyFromRoutineText; +} + + /* * error context callback for COPY FROM * @@ -1381,7 +1532,6 @@ BeginCopyFrom(ParseState *pstate, num_defaults; FmgrInfo *in_functions; Oid *typioparams; - Oid in_func_oid; int *defmap; ExprState **defexprs; MemoryContext oldcontext; @@ -1413,6 +1563,9 @@ BeginCopyFrom(ParseState *pstate, /* Extract options from the statement node tree */ ProcessCopyOptions(pstate, &cstate->opts, true /* is_from */ , options); + /* Set format routine */ + cstate->routine = CopyFromGetRoutine(cstate->opts); + /* Process the target relation */ cstate->rel = rel; @@ -1566,25 +1719,6 @@ BeginCopyFrom(ParseState *pstate, cstate->raw_buf_index = cstate->raw_buf_len = 0; cstate->raw_reached_eof = false; - if (!cstate->opts.binary) - { - /* - * If encoding conversion is needed, we need another buffer to hold - * the converted input data. Otherwise, we can just point input_buf - * to the same buffer as raw_buf. - */ - if (cstate->need_transcoding) - { - cstate->input_buf = (char *) palloc(INPUT_BUF_SIZE + 1); - cstate->input_buf_index = cstate->input_buf_len = 0; - } - else - cstate->input_buf = cstate->raw_buf; - cstate->input_reached_eof = false; - - initStringInfo(&cstate->line_buf); - } - initStringInfo(&cstate->attribute_buf); /* Assign range table and rteperminfos, we'll need them in CopyFrom. */ @@ -1617,23 +1751,9 @@ BeginCopyFrom(ParseState *pstate, continue; /* Fetch the input function and typioparam info */ - if (cstate->opts.binary) - { - getTypeBinaryInputInfo(att->atttypid, - &in_func_oid, &typioparams[attnum - 1]); - fmgr_info(in_func_oid, &in_functions[attnum - 1]); - } - else if (cstate->routine) - cstate->routine->CopyFromInFunc(cstate, att->atttypid, - &in_functions[attnum - 1], - &typioparams[attnum - 1]); - - else - { - getTypeInputInfo(att->atttypid, - &in_func_oid, &typioparams[attnum - 1]); - fmgr_info(in_func_oid, &in_functions[attnum - 1]); - } + cstate->routine->CopyFromInFunc(cstate, att->atttypid, + &in_functions[attnum - 1], + &typioparams[attnum - 1]); /* Get default info if available */ defexprs[attnum - 1] = NULL; @@ -1768,23 +1888,7 @@ BeginCopyFrom(ParseState *pstate, pgstat_progress_update_multi_param(3, progress_cols, progress_vals); - if (cstate->opts.binary) - { - /* Read and verify binary header */ - ReceiveCopyBinaryHeader(cstate); - } - else if (cstate->routine) - { - cstate->routine->CopyFromStart(cstate, tupDesc); - } - else - { - /* create workspace for CopyReadAttributes results */ - AttrNumber attr_count = list_length(cstate->attnumlist); - - cstate->max_fields = attr_count; - cstate->raw_fields = (char **) palloc(attr_count * sizeof(char *)); - } + cstate->routine->CopyFromStart(cstate, tupDesc); MemoryContextSwitchTo(oldcontext); @@ -1797,8 +1901,7 @@ BeginCopyFrom(ParseState *pstate, void EndCopyFrom(CopyFromState cstate) { - if (cstate->routine) - cstate->routine->CopyFromEnd(cstate); + cstate->routine->CopyFromEnd(cstate); /* No COPY FROM related resources except memory. */ if (cstate->is_program) diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c index 92b8d5e72d5..90824b47785 100644 --- a/src/backend/commands/copyfromparse.c +++ b/src/backend/commands/copyfromparse.c @@ -149,10 +149,10 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; /* non-export function prototypes */ -static bool CopyReadLine(CopyFromState cstate); -static bool CopyReadLineText(CopyFromState cstate); -static int CopyReadAttributesText(CopyFromState cstate); -static int CopyReadAttributesCSV(CopyFromState cstate); +static inline bool CopyReadLine(CopyFromState cstate, bool is_csv); +static inline bool CopyReadLineText(CopyFromState cstate, bool is_csv); +static inline int CopyReadAttributesText(CopyFromState cstate); +static inline int CopyReadAttributesCSV(CopyFromState cstate); static Datum CopyReadBinaryAttribute(CopyFromState cstate, FmgrInfo *flinfo, Oid typioparam, int32 typmod, bool *isnull); @@ -750,8 +750,8 @@ CopyReadBinaryData(CopyFromState cstate, char *dest, int nbytes) * * NOTE: force_not_null option are not applied to the returned fields. */ -bool -NextCopyFromRawFields(CopyFromState cstate, char ***fields, int *nfields) +static inline bool +NextCopyFromRawFields(CopyFromState cstate, char ***fields, int *nfields, bool is_csv) { int fldct; bool done; @@ -768,13 +768,17 @@ NextCopyFromRawFields(CopyFromState cstate, char ***fields, int *nfields) tupDesc = RelationGetDescr(cstate->rel); cstate->cur_lineno++; - done = CopyReadLine(cstate); + done = CopyReadLine(cstate, is_csv); if (cstate->opts.header_line == COPY_HEADER_MATCH) { int fldnum; - if (cstate->opts.csv_mode) + /* + * is_csv will be optimized away by compiler, as argument is + * constant at caller. + */ + if (is_csv) fldct = CopyReadAttributesCSV(cstate); else fldct = CopyReadAttributesText(cstate); @@ -818,7 +822,7 @@ NextCopyFromRawFields(CopyFromState cstate, char ***fields, int *nfields) cstate->cur_lineno++; /* Actually read the line into memory here */ - done = CopyReadLine(cstate); + done = CopyReadLine(cstate, is_csv); /* * EOF at start of line means we're done. If we see EOF after some @@ -828,8 +832,13 @@ NextCopyFromRawFields(CopyFromState cstate, char ***fields, int *nfields) if (done && cstate->line_buf.len == 0) return false; - /* Parse the line into de-escaped field values */ - if (cstate->opts.csv_mode) + /* + * Parse the line into de-escaped field values + * + * is_csv will be optimized away by compiler, as argument is constant at + * caller. + */ + if (is_csv) fldct = CopyReadAttributesCSV(cstate); else fldct = CopyReadAttributesText(cstate); @@ -839,6 +848,267 @@ NextCopyFromRawFields(CopyFromState cstate, char ***fields, int *nfields) return true; } +/* + * CopyFromTextLikeOneRow + * + * Copy one row to a set of `values` and `nulls` for the text and CSV + * formats. + * + * Workhorse for CopyFromTextOneRow() and CopyFromCSVOneRow(). + */ +static inline bool +CopyFromTextLikeOneRow(CopyFromState cstate, + ExprContext *econtext, + Datum *values, + bool *nulls, + bool is_csv) +{ + TupleDesc tupDesc; + AttrNumber attr_count; + FmgrInfo *in_functions = cstate->in_functions; + Oid *typioparams = cstate->typioparams; + ExprState **defexprs = cstate->defexprs; + char **field_strings; + ListCell *cur; + int fldct; + int fieldno; + char *string; + + tupDesc = RelationGetDescr(cstate->rel); + attr_count = list_length(cstate->attnumlist); + + /* read raw fields in the next line */ + if (!NextCopyFromRawFields(cstate, &field_strings, &fldct, is_csv)) + return false; + + /* check for overflowing fields */ + if (attr_count > 0 && fldct > attr_count) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("extra data after last expected column"))); + + fieldno = 0; + + /* Loop to read the user attributes on the line. */ + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + int m = attnum - 1; + Form_pg_attribute att = TupleDescAttr(tupDesc, m); + + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("missing data for column \"%s\"", + NameStr(att->attname)))); + string = field_strings[fieldno++]; + + if (cstate->convert_select_flags && + !cstate->convert_select_flags[m]) + { + /* ignore input field, leaving column as NULL */ + continue; + } + + if (is_csv) + { + if (string == NULL && + cstate->opts.force_notnull_flags[m]) + { + /* + * FORCE_NOT_NULL option is set and column is NULL - convert + * it to the NULL string. + */ + string = cstate->opts.null_print; + } + else if (string != NULL && cstate->opts.force_null_flags[m] + && strcmp(string, cstate->opts.null_print) == 0) + { + /* + * FORCE_NULL option is set and column matches the NULL + * string. It must have been quoted, or otherwise the string + * would already have been set to NULL. Convert it to NULL as + * specified. + */ + string = NULL; + } + } + + cstate->cur_attname = NameStr(att->attname); + cstate->cur_attval = string; + + if (string != NULL) + nulls[m] = false; + + if (cstate->defaults[m]) + { + /* + * The caller must supply econtext and have switched into the + * per-tuple memory context in it. + */ + Assert(econtext != NULL); + Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory); + + values[m] = ExecEvalExpr(defexprs[m], econtext, &nulls[m]); + } + + /* + * If ON_ERROR is specified with IGNORE, skip rows with soft errors + */ + else if (!InputFunctionCallSafe(&in_functions[m], + string, + typioparams[m], + att->atttypmod, + (Node *) cstate->escontext, + &values[m])) + { + Assert(cstate->opts.on_error != COPY_ON_ERROR_STOP); + + cstate->num_errors++; + + if (cstate->opts.log_verbosity == COPY_LOG_VERBOSITY_VERBOSE) + { + /* + * Since we emit line number and column info in the below + * notice message, we suppress error context information other + * than the relation name. + */ + Assert(!cstate->relname_only); + cstate->relname_only = true; + + if (cstate->cur_attval) + { + char *attval; + + attval = CopyLimitPrintoutLength(cstate->cur_attval); + ereport(NOTICE, + errmsg("skipping row due to data type incompatibility at line %llu for column %s: \"%s\"", + (unsigned long long) cstate->cur_lineno, + cstate->cur_attname, + attval)); + pfree(attval); + } + else + ereport(NOTICE, + errmsg("skipping row due to data type incompatibility at line %llu for column %s: null input", + (unsigned long long) cstate->cur_lineno, + cstate->cur_attname)); + + /* reset relname_only */ + cstate->relname_only = false; + } + + return true; + } + + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; + } + + Assert(fieldno == attr_count); + + return true; +} + + +/* + * CopyFromTextOneRow + * + * Per-row callback for COPY FROM with text format. + */ +bool +CopyFromTextOneRow(CopyFromState cstate, + ExprContext *econtext, + Datum *values, + bool *nulls) +{ + return CopyFromTextLikeOneRow(cstate, econtext, values, nulls, false); +} + +/* + * CopyFromCSVOneRow + * + * Per-row callback for COPY FROM with CSV format. + */ +bool +CopyFromCSVOneRow(CopyFromState cstate, + ExprContext *econtext, + Datum *values, + bool *nulls) +{ + return CopyFromTextLikeOneRow(cstate, econtext, values, nulls, true); +} + +/* + * CopyFromBinaryOneRow + * + * Copy one row to a set of `values` and `nulls` for the binary format. + */ +bool +CopyFromBinaryOneRow(CopyFromState cstate, ExprContext *econtext, + Datum *values, bool *nulls) +{ + TupleDesc tupDesc; + AttrNumber attr_count; + FmgrInfo *in_functions = cstate->in_functions; + Oid *typioparams = cstate->typioparams; + int16 fld_count; + ListCell *cur; + + tupDesc = RelationGetDescr(cstate->rel); + attr_count = list_length(cstate->attnumlist); + + cstate->cur_lineno++; + + if (!CopyGetInt16(cstate, &fld_count)) + { + /* EOF detected (end of file, or protocol-level EOF) */ + return false; + } + + if (fld_count == -1) + { + /* + * Received EOF marker. Wait for the protocol-level EOF, and complain + * if it doesn't come immediately. In COPY FROM STDIN, this ensures + * that we correctly handle CopyFail, if client chooses to send that + * now. When copying from file, we could ignore the rest of the file + * like in text mode, but we choose to be consistent with the COPY + * FROM STDIN case. + */ + char dummy; + + if (CopyReadBinaryData(cstate, &dummy, 1) > 0) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("received copy data after EOF marker"))); + return false; + } + + if (fld_count != attr_count) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("row field count is %d, expected %d", + (int) fld_count, attr_count))); + + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + int m = attnum - 1; + Form_pg_attribute att = TupleDescAttr(tupDesc, m); + + cstate->cur_attname = NameStr(att->attname); + values[m] = CopyReadBinaryAttribute(cstate, + &in_functions[m], + typioparams[m], + att->atttypmod, + &nulls[m]); + cstate->cur_attname = NULL; + } + + return true; +} + /* * Read next tuple from file for COPY FROM. Return false if no more tuples. * @@ -856,221 +1126,21 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext, { TupleDesc tupDesc; AttrNumber num_phys_attrs, - attr_count, num_defaults = cstate->num_defaults; - FmgrInfo *in_functions = cstate->in_functions; - Oid *typioparams = cstate->typioparams; int i; int *defmap = cstate->defmap; ExprState **defexprs = cstate->defexprs; tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; - attr_count = list_length(cstate->attnumlist); /* Initialize all values for row to NULL */ MemSet(values, 0, num_phys_attrs * sizeof(Datum)); MemSet(nulls, true, num_phys_attrs * sizeof(bool)); MemSet(cstate->defaults, false, num_phys_attrs * sizeof(bool)); - if (!cstate->opts.binary) - { - char **field_strings; - ListCell *cur; - int fldct; - int fieldno; - char *string; - - /* read raw fields in the next line */ - if (!NextCopyFromRawFields(cstate, &field_strings, &fldct)) - return false; - - /* check for overflowing fields */ - if (attr_count > 0 && fldct > attr_count) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("extra data after last expected column"))); - - fieldno = 0; - - /* Loop to read the user attributes on the line. */ - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); - int m = attnum - 1; - Form_pg_attribute att = TupleDescAttr(tupDesc, m); - - if (fieldno >= fldct) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("missing data for column \"%s\"", - NameStr(att->attname)))); - string = field_strings[fieldno++]; - - if (cstate->convert_select_flags && - !cstate->convert_select_flags[m]) - { - /* ignore input field, leaving column as NULL */ - continue; - } - - if (cstate->opts.csv_mode) - { - if (string == NULL && - cstate->opts.force_notnull_flags[m]) - { - /* - * FORCE_NOT_NULL option is set and column is NULL - - * convert it to the NULL string. - */ - string = cstate->opts.null_print; - } - else if (string != NULL && cstate->opts.force_null_flags[m] - && strcmp(string, cstate->opts.null_print) == 0) - { - /* - * FORCE_NULL option is set and column matches the NULL - * string. It must have been quoted, or otherwise the - * string would already have been set to NULL. Convert it - * to NULL as specified. - */ - string = NULL; - } - } - - cstate->cur_attname = NameStr(att->attname); - cstate->cur_attval = string; - - if (string != NULL) - nulls[m] = false; - - if (cstate->defaults[m]) - { - /* - * The caller must supply econtext and have switched into the - * per-tuple memory context in it. - */ - Assert(econtext != NULL); - Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory); - - values[m] = ExecEvalExpr(defexprs[m], econtext, &nulls[m]); - } - - /* - * If ON_ERROR is specified with IGNORE, skip rows with soft - * errors - */ - else if (!InputFunctionCallSafe(&in_functions[m], - string, - typioparams[m], - att->atttypmod, - (Node *) cstate->escontext, - &values[m])) - { - Assert(cstate->opts.on_error != COPY_ON_ERROR_STOP); - - cstate->num_errors++; - - if (cstate->opts.log_verbosity == COPY_LOG_VERBOSITY_VERBOSE) - { - /* - * Since we emit line number and column info in the below - * notice message, we suppress error context information - * other than the relation name. - */ - Assert(!cstate->relname_only); - cstate->relname_only = true; - - if (cstate->cur_attval) - { - char *attval; - - attval = CopyLimitPrintoutLength(cstate->cur_attval); - ereport(NOTICE, - errmsg("skipping row due to data type incompatibility at line %llu for column %s: \"%s\"", - (unsigned long long) cstate->cur_lineno, - cstate->cur_attname, - attval)); - pfree(attval); - } - else - ereport(NOTICE, - errmsg("skipping row due to data type incompatibility at line %llu for column %s: null input", - (unsigned long long) cstate->cur_lineno, - cstate->cur_attname)); - - /* reset relname_only */ - cstate->relname_only = false; - } - - return true; - } - - cstate->cur_attname = NULL; - cstate->cur_attval = NULL; - } - - Assert(fieldno == attr_count); - } - else if (cstate->routine) - { - if (!cstate->routine->CopyFromOneRow(cstate, econtext, values, nulls)) - return false; - } - else - { - /* binary */ - int16 fld_count; - ListCell *cur; - - cstate->cur_lineno++; - - if (!CopyGetInt16(cstate, &fld_count)) - { - /* EOF detected (end of file, or protocol-level EOF) */ - return false; - } - - if (fld_count == -1) - { - /* - * Received EOF marker. Wait for the protocol-level EOF, and - * complain if it doesn't come immediately. In COPY FROM STDIN, - * this ensures that we correctly handle CopyFail, if client - * chooses to send that now. When copying from file, we could - * ignore the rest of the file like in text mode, but we choose to - * be consistent with the COPY FROM STDIN case. - */ - char dummy; - - if (CopyReadBinaryData(cstate, &dummy, 1) > 0) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("received copy data after EOF marker"))); - return false; - } - - if (fld_count != attr_count) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("row field count is %d, expected %d", - (int) fld_count, attr_count))); - - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); - int m = attnum - 1; - Form_pg_attribute att = TupleDescAttr(tupDesc, m); - - cstate->cur_attname = NameStr(att->attname); - values[m] = CopyReadBinaryAttribute(cstate, - &in_functions[m], - typioparams[m], - att->atttypmod, - &nulls[m]); - cstate->cur_attname = NULL; - } - } + if (!cstate->routine->CopyFromOneRow(cstate, econtext, values, nulls)) + return false; /* * Now compute and insert any defaults available for the columns not @@ -1100,8 +1170,8 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext, * by newline. The terminating newline or EOF marker is not included * in the final value of line_buf. */ -static bool -CopyReadLine(CopyFromState cstate) +static inline bool +CopyReadLine(CopyFromState cstate, bool is_csv) { bool result; @@ -1109,7 +1179,7 @@ CopyReadLine(CopyFromState cstate) cstate->line_buf_valid = false; /* Parse data and transfer into line_buf */ - result = CopyReadLineText(cstate); + result = CopyReadLineText(cstate, is_csv); if (result) { @@ -1176,8 +1246,8 @@ CopyReadLine(CopyFromState cstate) /* * CopyReadLineText - inner loop of CopyReadLine for text mode */ -static bool -CopyReadLineText(CopyFromState cstate) +static inline bool +CopyReadLineText(CopyFromState cstate, bool is_csv) { char *copy_input_buf; int input_buf_ptr; @@ -1193,7 +1263,11 @@ CopyReadLineText(CopyFromState cstate) char quotec = '\0'; char escapec = '\0'; - if (cstate->opts.csv_mode) + /* + * is_csv will be optimized away by compiler, as argument is constant at + * caller. + */ + if (is_csv) { quotec = cstate->opts.quote[0]; escapec = cstate->opts.escape[0]; @@ -1270,7 +1344,11 @@ CopyReadLineText(CopyFromState cstate) prev_raw_ptr = input_buf_ptr; c = copy_input_buf[input_buf_ptr++]; - if (cstate->opts.csv_mode) + /* + * is_csv will be optimized away by compiler, as argument is constant + * at caller. + */ + if (is_csv) { /* * If character is '\\' or '\r', we may need to look ahead below. @@ -1309,7 +1387,7 @@ CopyReadLineText(CopyFromState cstate) } /* Process \r */ - if (c == '\r' && (!cstate->opts.csv_mode || !in_quote)) + if (c == '\r' && (!is_csv || !in_quote)) { /* Check for \r\n on first line, _and_ handle \r\n. */ if (cstate->eol_type == EOL_UNKNOWN || @@ -1337,10 +1415,10 @@ CopyReadLineText(CopyFromState cstate) if (cstate->eol_type == EOL_CRNL) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - !cstate->opts.csv_mode ? + !is_csv ? errmsg("literal carriage return found in data") : errmsg("unquoted carriage return found in data"), - !cstate->opts.csv_mode ? + !is_csv ? errhint("Use \"\\r\" to represent carriage return.") : errhint("Use quoted CSV field to represent carriage return."))); @@ -1354,10 +1432,10 @@ CopyReadLineText(CopyFromState cstate) else if (cstate->eol_type == EOL_NL) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - !cstate->opts.csv_mode ? + !is_csv ? errmsg("literal carriage return found in data") : errmsg("unquoted carriage return found in data"), - !cstate->opts.csv_mode ? + !is_csv ? errhint("Use \"\\r\" to represent carriage return.") : errhint("Use quoted CSV field to represent carriage return."))); /* If reach here, we have found the line terminator */ @@ -1365,15 +1443,15 @@ CopyReadLineText(CopyFromState cstate) } /* Process \n */ - if (c == '\n' && (!cstate->opts.csv_mode || !in_quote)) + if (c == '\n' && (!is_csv || !in_quote)) { if (cstate->eol_type == EOL_CR || cstate->eol_type == EOL_CRNL) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - !cstate->opts.csv_mode ? + !is_csv ? errmsg("literal newline found in data") : errmsg("unquoted newline found in data"), - !cstate->opts.csv_mode ? + !is_csv ? errhint("Use \"\\n\" to represent newline.") : errhint("Use quoted CSV field to represent newline."))); cstate->eol_type = EOL_NL; /* in case not set yet */ @@ -1385,7 +1463,7 @@ CopyReadLineText(CopyFromState cstate) * In CSV mode, we only recognize \. alone on a line. This is because * \. is a valid CSV data value. */ - if (c == '\\' && (!cstate->opts.csv_mode || first_char_in_line)) + if (c == '\\' && (!is_csv || first_char_in_line)) { char c2; @@ -1418,7 +1496,11 @@ CopyReadLineText(CopyFromState cstate) if (c2 == '\n') { - if (!cstate->opts.csv_mode) + /* + * is_csv will be optimized away by compiler, as + * argument is constant at caller. + */ + if (!is_csv) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("end-of-copy marker does not match previous newline style"))); @@ -1427,7 +1509,11 @@ CopyReadLineText(CopyFromState cstate) } else if (c2 != '\r') { - if (!cstate->opts.csv_mode) + /* + * is_csv will be optimized away by compiler, as + * argument is constant at caller. + */ + if (!is_csv) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("end-of-copy marker corrupt"))); @@ -1443,7 +1529,11 @@ CopyReadLineText(CopyFromState cstate) if (c2 != '\r' && c2 != '\n') { - if (!cstate->opts.csv_mode) + /* + * is_csv will be optimized away by compiler, as argument + * is constant at caller. + */ + if (!is_csv) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("end-of-copy marker corrupt"))); @@ -1472,7 +1562,7 @@ CopyReadLineText(CopyFromState cstate) result = true; /* report EOF */ break; } - else if (!cstate->opts.csv_mode) + else if (!is_csv) { /* * If we are here, it means we found a backslash followed by diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index ff19c457abf..c7f69ba606d 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -128,6 +128,321 @@ static void CopySendEndOfRow(CopyToState cstate); static void CopySendInt32(CopyToState cstate, int32 val); static void CopySendInt16(CopyToState cstate, int16 val); +/* + * CopyToRoutine implementations. + */ + +/* + * CopyToTextLikeSendEndOfRow + * + * Apply line terminations for a line sent in text or CSV format depending + * on the destination, then send the end of a row. + */ +static inline void +CopyToTextLikeSendEndOfRow(CopyToState cstate) +{ + switch (cstate->copy_dest) + { + case COPY_FILE: + /* Default line termination depends on platform */ +#ifndef WIN32 + CopySendChar(cstate, '\n'); +#else + CopySendString(cstate, "\r\n"); +#endif + break; + case COPY_FRONTEND: + /* The FE/BE protocol uses \n as newline for all platforms */ + CopySendChar(cstate, '\n'); + break; + default: + break; + } + + /* Now take the actions related to the end of a row */ + CopySendEndOfRow(cstate); +} + +/* + * CopyToTextLikeStart + * + * Start of COPY TO for text and CSV format. + */ +static void +CopyToTextLikeStart(CopyToState cstate, TupleDesc tupDesc) +{ + /* + * For non-binary copy, we need to convert null_print to file encoding, + * because it will be sent directly with CopySendString. + */ + if (cstate->need_transcoding) + cstate->opts.null_print_client = pg_server_to_any(cstate->opts.null_print, + cstate->opts.null_print_len, + cstate->file_encoding); + + /* if a header has been requested send the line */ + if (cstate->opts.header_line) + { + ListCell *cur; + bool hdr_delim = false; + + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + char *colname; + + if (hdr_delim) + CopySendChar(cstate, cstate->opts.delim[0]); + hdr_delim = true; + + colname = NameStr(TupleDescAttr(tupDesc, attnum - 1)->attname); + + if (cstate->opts.csv_mode) + CopyAttributeOutCSV(cstate, colname, false); + else + CopyAttributeOutText(cstate, colname); + } + + CopyToTextLikeSendEndOfRow(cstate); + } +} + +/* + * CopyToTextLikeOutFunc + * + * Assign output function data for a relation's attribute in text/CSV format. + */ +static void +CopyToTextLikeOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo) +{ + Oid func_oid; + bool is_varlena; + + /* Set output function for an attribute */ + getTypeOutputInfo(atttypid, &func_oid, &is_varlena); + fmgr_info(func_oid, finfo); +} + + +/* + * CopyToTextLikeOneRow + * + * Process one row for text/CSV format. + * + * Workhorse for CopyToTextOneRow() and CopyToCSVOneRow(). + */ +static inline void +CopyToTextLikeOneRow(CopyToState cstate, + TupleTableSlot *slot, + bool is_csv) +{ + bool need_delim = false; + FmgrInfo *out_functions = cstate->out_functions; + ListCell *cur; + + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + Datum value = slot->tts_values[attnum - 1]; + bool isnull = slot->tts_isnull[attnum - 1]; + + if (need_delim) + CopySendChar(cstate, cstate->opts.delim[0]); + need_delim = true; + + if (isnull) + { + CopySendString(cstate, cstate->opts.null_print_client); + } + else + { + char *string; + + string = OutputFunctionCall(&out_functions[attnum - 1], + value); + + /* + * is_csv will be optimized away by compiler, as argument is + * constant at caller. + */ + if (is_csv) + CopyAttributeOutCSV(cstate, string, + cstate->opts.force_quote_flags[attnum - 1]); + else + CopyAttributeOutText(cstate, string); + } + } + + CopyToTextLikeSendEndOfRow(cstate); +} + +/* + * CopyToTextOneRow + * + * Per-row callback for COPY TO with text format. + */ +static void +CopyToTextOneRow(CopyToState cstate, TupleTableSlot *slot) +{ + CopyToTextLikeOneRow(cstate, slot, false); +} + +/* + * CopyToTextOneRow + * + * Per-row callback for COPY TO with CSV format. + */ +static void +CopyToCSVOneRow(CopyToState cstate, TupleTableSlot *slot) +{ + CopyToTextLikeOneRow(cstate, slot, true); +} + +/* + * CopyToTextLikeEnd + * + * End of COPY TO for text/CSV format. + */ +static void +CopyToTextLikeEnd(CopyToState cstate) +{ + /* Nothing to do here */ +} + +/* + * CopyToRoutine implementation for "binary". + */ + +/* + * CopyToBinaryStart + * + * Start of COPY TO for binary format. + */ +static void +CopyToBinaryStart(CopyToState cstate, TupleDesc tupDesc) +{ + /* Generate header for a binary copy */ + int32 tmp; + + /* Signature */ + CopySendData(cstate, BinarySignature, 11); + /* Flags field */ + tmp = 0; + CopySendInt32(cstate, tmp); + /* No header extension */ + tmp = 0; + CopySendInt32(cstate, tmp); +} + +/* + * CopyToBinaryOutFunc + * + * Assign output function data for a relation's attribute in binary format. + */ +static void +CopyToBinaryOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo) +{ + Oid func_oid; + bool is_varlena; + + /* Set output function for an attribute */ + getTypeBinaryOutputInfo(atttypid, &func_oid, &is_varlena); + fmgr_info(func_oid, finfo); +} + +/* + * CopyToBinaryOneRow + * + * Process one row for binary format. + */ +static void +CopyToBinaryOneRow(CopyToState cstate, TupleTableSlot *slot) +{ + FmgrInfo *out_functions = cstate->out_functions; + ListCell *cur; + + /* Binary per-tuple header */ + CopySendInt16(cstate, list_length(cstate->attnumlist)); + + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + Datum value = slot->tts_values[attnum - 1]; + bool isnull = slot->tts_isnull[attnum - 1]; + + if (isnull) + { + CopySendInt32(cstate, -1); + } + else + { + bytea *outputbytes; + + outputbytes = SendFunctionCall(&out_functions[attnum - 1], + value); + CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); + CopySendData(cstate, VARDATA(outputbytes), + VARSIZE(outputbytes) - VARHDRSZ); + } + } + + CopySendEndOfRow(cstate); +} + +/* + * CopyToBinaryEnd + * + * End of COPY TO for binary format. + */ +static void +CopyToBinaryEnd(CopyToState cstate) +{ + /* Generate trailer for a binary copy */ + CopySendInt16(cstate, -1); + /* Need to flush out the trailer */ + CopySendEndOfRow(cstate); +} + +/* + * CSV and text share the same implementation, at the exception of the + * output representation and per-row callbacks. + */ +static const CopyToRoutine CopyToRoutineText = { + .CopyToStart = CopyToTextLikeStart, + .CopyToOutFunc = CopyToTextLikeOutFunc, + .CopyToOneRow = CopyToTextOneRow, + .CopyToEnd = CopyToTextLikeEnd, +}; + +static const CopyToRoutine CopyToRoutineCSV = { + .CopyToStart = CopyToTextLikeStart, + .CopyToOutFunc = CopyToTextLikeOutFunc, + .CopyToOneRow = CopyToCSVOneRow, + .CopyToEnd = CopyToTextLikeEnd, +}; + +static const CopyToRoutine CopyToRoutineBinary = { + .CopyToStart = CopyToBinaryStart, + .CopyToOutFunc = CopyToBinaryOutFunc, + .CopyToOneRow = CopyToBinaryOneRow, + .CopyToEnd = CopyToBinaryEnd, +}; + +/* + * Define the COPY TO routines to use for a format. This should be called + * after options are parsed. + */ +static const CopyToRoutine * +CopyToGetRoutine(CopyFormatOptions opts) +{ + if (opts.csv_mode) + return &CopyToRoutineCSV; + else if (opts.binary) + return &CopyToRoutineBinary; + + /* default is text */ + return &CopyToRoutineText; +} /* * Send copy start/stop messages for frontend copies. These have changed @@ -195,16 +510,6 @@ CopySendEndOfRow(CopyToState cstate) switch (cstate->copy_dest) { case COPY_FILE: - if (!cstate->opts.binary) - { - /* Default line termination depends on platform */ -#ifndef WIN32 - CopySendChar(cstate, '\n'); -#else - CopySendString(cstate, "\r\n"); -#endif - } - if (fwrite(fe_msgbuf->data, fe_msgbuf->len, 1, cstate->copy_file) != 1 || ferror(cstate->copy_file)) @@ -239,10 +544,6 @@ CopySendEndOfRow(CopyToState cstate) } break; case COPY_FRONTEND: - /* The FE/BE protocol uses \n as newline for all platforms */ - if (!cstate->opts.binary) - CopySendChar(cstate, '\n'); - /* Dump the accumulated row as one CopyData message */ (void) pq_putmessage(PqMsg_CopyData, fe_msgbuf->data, fe_msgbuf->len); break; @@ -430,6 +731,9 @@ BeginCopyTo(ParseState *pstate, /* Extract options from the statement node tree */ ProcessCopyOptions(pstate, &cstate->opts, false /* is_from */ , options); + /* Set format routine */ + cstate->routine = CopyToGetRoutine(cstate->opts); + /* Process the source/target relation or query */ if (rel) { @@ -770,27 +1074,10 @@ DoCopyTo(CopyToState cstate) foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); - Oid out_func_oid; - bool isvarlena; Form_pg_attribute attr = TupleDescAttr(tupDesc, attnum - 1); - if (cstate->opts.binary) - { - getTypeBinaryOutputInfo(attr->atttypid, - &out_func_oid, - &isvarlena); - fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); - } - else if (cstate->routine) - cstate->routine->CopyToOutFunc(cstate, attr->atttypid, - &cstate->out_functions[attnum - 1]); - else - { - getTypeOutputInfo(attr->atttypid, - &out_func_oid, - &isvarlena); - fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); - } + cstate->routine->CopyToOutFunc(cstate, attr->atttypid, + &cstate->out_functions[attnum - 1]); } /* @@ -803,58 +1090,7 @@ DoCopyTo(CopyToState cstate) "COPY TO", ALLOCSET_DEFAULT_SIZES); - if (cstate->opts.binary) - { - /* Generate header for a binary copy */ - int32 tmp; - - /* Signature */ - CopySendData(cstate, BinarySignature, 11); - /* Flags field */ - tmp = 0; - CopySendInt32(cstate, tmp); - /* No header extension */ - tmp = 0; - CopySendInt32(cstate, tmp); - } - else if (cstate->routine) - cstate->routine->CopyToStart(cstate, tupDesc); - else - { - /* - * For non-binary copy, we need to convert null_print to file - * encoding, because it will be sent directly with CopySendString. - */ - if (cstate->need_transcoding) - cstate->opts.null_print_client = pg_server_to_any(cstate->opts.null_print, - cstate->opts.null_print_len, - cstate->file_encoding); - - /* if a header has been requested send the line */ - if (cstate->opts.header_line) - { - bool hdr_delim = false; - - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); - char *colname; - - if (hdr_delim) - CopySendChar(cstate, cstate->opts.delim[0]); - hdr_delim = true; - - colname = NameStr(TupleDescAttr(tupDesc, attnum - 1)->attname); - - if (cstate->opts.csv_mode) - CopyAttributeOutCSV(cstate, colname, false); - else - CopyAttributeOutText(cstate, colname); - } - - CopySendEndOfRow(cstate); - } - } + cstate->routine->CopyToStart(cstate, tupDesc); if (cstate->rel) { @@ -893,15 +1129,7 @@ DoCopyTo(CopyToState cstate) processed = ((DR_copy *) cstate->queryDesc->dest)->processed; } - if (cstate->opts.binary) - { - /* Generate trailer for a binary copy */ - CopySendInt16(cstate, -1); - /* Need to flush out the trailer */ - CopySendEndOfRow(cstate); - } - else if (cstate->routine) - cstate->routine->CopyToEnd(cstate); + cstate->routine->CopyToEnd(cstate); MemoryContextDelete(cstate->rowcontext); @@ -917,11 +1145,7 @@ DoCopyTo(CopyToState cstate) static void CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot) { - bool need_delim = false; - FmgrInfo *out_functions = cstate->out_functions; MemoryContext oldcontext; - ListCell *cur; - char *string; MemoryContextReset(cstate->rowcontext); oldcontext = MemoryContextSwitchTo(cstate->rowcontext); @@ -929,65 +1153,7 @@ CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot) /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); - if (cstate->routine) - { - cstate->routine->CopyToOneRow(cstate, slot); - MemoryContextSwitchTo(oldcontext); - return; - } - - if (cstate->opts.binary) - { - /* Binary per-tuple header */ - CopySendInt16(cstate, list_length(cstate->attnumlist)); - } - - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); - Datum value = slot->tts_values[attnum - 1]; - bool isnull = slot->tts_isnull[attnum - 1]; - - if (!cstate->opts.binary) - { - if (need_delim) - CopySendChar(cstate, cstate->opts.delim[0]); - need_delim = true; - } - - if (isnull) - { - if (!cstate->opts.binary) - CopySendString(cstate, cstate->opts.null_print_client); - else - CopySendInt32(cstate, -1); - } - else - { - if (!cstate->opts.binary) - { - string = OutputFunctionCall(&out_functions[attnum - 1], - value); - if (cstate->opts.csv_mode) - CopyAttributeOutCSV(cstate, string, - cstate->opts.force_quote_flags[attnum - 1]); - else - CopyAttributeOutText(cstate, string); - } - else - { - bytea *outputbytes; - - outputbytes = SendFunctionCall(&out_functions[attnum - 1], - value); - CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); - CopySendData(cstate, VARDATA(outputbytes), - VARSIZE(outputbytes) - VARHDRSZ); - } - } - } - - CopySendEndOfRow(cstate); + cstate->routine->CopyToOneRow(cstate, slot); MemoryContextSwitchTo(oldcontext); } diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h index 141fd48dc10..ccfbdf0ee01 100644 --- a/src/include/commands/copy.h +++ b/src/include/commands/copy.h @@ -104,8 +104,6 @@ extern CopyFromState BeginCopyFrom(ParseState *pstate, Relation rel, Node *where extern void EndCopyFrom(CopyFromState cstate); extern bool NextCopyFrom(CopyFromState cstate, ExprContext *econtext, Datum *values, bool *nulls); -extern bool NextCopyFromRawFields(CopyFromState cstate, - char ***fields, int *nfields); extern void CopyFromErrorCallback(void *arg); extern char *CopyLimitPrintoutLength(const char *str); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 509b9e92a18..c11b5ff3cc0 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -187,4 +187,12 @@ typedef struct CopyFromStateData extern void ReceiveCopyBegin(CopyFromState cstate); extern void ReceiveCopyBinaryHeader(CopyFromState cstate); +/* Callbacks for CopyFromRoutine->CopyFromOneRow */ +extern bool CopyFromTextOneRow(CopyFromState cstate, ExprContext *econtext, + Datum *values, bool *nulls); +extern bool CopyFromCSVOneRow(CopyFromState cstate, ExprContext *econtext, + Datum *values, bool *nulls); +extern bool CopyFromBinaryOneRow(CopyFromState cstate, ExprContext *econtext, + Datum *values, bool *nulls); + #endif /* COPYFROM_INTERNAL_H */ -- 2.45.2 From 273918b50a2c64659376e4a9b8e42d2a6480abfb Mon Sep 17 00:00:00 2001 From: Sutou Kouhei <kou@clear-code.com> Date: Tue, 23 Jul 2024 17:39:41 +0900 Subject: [PATCH v19 3/5] Add support for adding custom COPY TO format This uses the handler approach like tablesample. The approach creates an internal function that returns an internal struct. In this case, a COPY TO handler returns a CopyToRoutine and a COPY FROM handler returns a CopyFromRoutine. This uses the same handler for COPY TO and COPY FROM. PostgreSQL calls a COPY TO/FROM handler with "is_from" argument. It's true for COPY FROM and false for COPY TO: copy_handler(true) returns CopyToRoutine copy_handler(false) returns CopyFromRoutine This also add a test module for custom COPY TO/FROM handler. --- src/backend/commands/copy.c | 96 ++++++++++++++--- src/backend/commands/copyfrom.c | 4 +- src/backend/commands/copyto.c | 4 +- src/backend/nodes/Makefile | 1 + src/backend/nodes/gen_node_support.pl | 2 + src/backend/utils/adt/pseudotypes.c | 1 + src/include/catalog/pg_proc.dat | 6 ++ src/include/catalog/pg_type.dat | 6 ++ src/include/commands/copy.h | 2 + src/include/commands/copyapi.h | 4 + src/include/nodes/meson.build | 1 + src/test/modules/Makefile | 1 + src/test/modules/meson.build | 1 + src/test/modules/test_copy_format/.gitignore | 4 + src/test/modules/test_copy_format/Makefile | 23 ++++ .../expected/test_copy_format.out | 21 ++++ src/test/modules/test_copy_format/meson.build | 33 ++++++ .../test_copy_format/sql/test_copy_format.sql | 6 ++ .../test_copy_format--1.0.sql | 8 ++ .../test_copy_format/test_copy_format.c | 100 ++++++++++++++++++ .../test_copy_format/test_copy_format.control | 4 + 21 files changed, 313 insertions(+), 15 deletions(-) create mode 100644 src/test/modules/test_copy_format/.gitignore create mode 100644 src/test/modules/test_copy_format/Makefile create mode 100644 src/test/modules/test_copy_format/expected/test_copy_format.out create mode 100644 src/test/modules/test_copy_format/meson.build create mode 100644 src/test/modules/test_copy_format/sql/test_copy_format.sql create mode 100644 src/test/modules/test_copy_format/test_copy_format--1.0.sql create mode 100644 src/test/modules/test_copy_format/test_copy_format.c create mode 100644 src/test/modules/test_copy_format/test_copy_format.control diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index df7a4a21c94..e5137e7bb3d 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -32,6 +32,7 @@ #include "parser/parse_coerce.h" #include "parser/parse_collate.h" #include "parser/parse_expr.h" +#include "parser/parse_func.h" #include "parser/parse_relation.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -439,6 +440,87 @@ defGetCopyLogVerbosityChoice(DefElem *def, ParseState *pstate) return COPY_LOG_VERBOSITY_DEFAULT; /* keep compiler quiet */ } +/* + * Process the "format" option. + * + * This function checks whether the option value is a built-in format such as + * "text" and "csv" or not. If the option value isn't a built-in format, this + * function finds a COPY format handler that returns a CopyToRoutine (for + * is_from == false) or CopyFromRountine (for is_from == true). If no COPY + * format handler is found, this function reports an error. + */ +static void +ProcessCopyOptionFormat(ParseState *pstate, + CopyFormatOptions *opts_out, + bool is_from, + DefElem *defel) +{ + char *format; + Oid funcargtypes[1]; + Oid handlerOid = InvalidOid; + Datum datum; + Node *routine; + + format = defGetString(defel); + + /* built-in formats */ + if (strcmp(format, "text") == 0) + /* default format */ return; + else if (strcmp(format, "csv") == 0) + { + opts_out->csv_mode = true; + return; + } + else if (strcmp(format, "binary") == 0) + { + opts_out->binary = true; + return; + } + + /* custom format */ + funcargtypes[0] = INTERNALOID; + handlerOid = LookupFuncName(list_make1(makeString(format)), 1, + funcargtypes, true); + if (!OidIsValid(handlerOid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("COPY format \"%s\" not recognized", format), + parser_errposition(pstate, defel->location))); + + datum = OidFunctionCall1(handlerOid, BoolGetDatum(is_from)); + routine = (Node *) DatumGetPointer(datum); + if (is_from) + { + if (routine == NULL || !IsA(routine, CopyFromRoutine)) + ereport( + ERROR, + (errcode( + ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("COPY handler function " + "%s(%u) did not return a " + "CopyFromRoutine struct", + format, handlerOid), + parser_errposition( + pstate, defel->location))); + } + else + { + if (routine == NULL || !IsA(routine, CopyToRoutine)) + ereport( + ERROR, + (errcode( + ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("COPY handler function " + "%s(%u) did not return a " + "CopyToRoutine struct", + format, handlerOid), + parser_errposition( + pstate, defel->location))); + } + + opts_out->routine = routine; +} + /* * Process the statement option list for COPY. * @@ -481,22 +563,10 @@ ProcessCopyOptions(ParseState *pstate, if (strcmp(defel->defname, "format") == 0) { - char *fmt = defGetString(defel); - if (format_specified) errorConflictingDefElem(defel, pstate); format_specified = true; - if (strcmp(fmt, "text") == 0) - /* default format */ ; - else if (strcmp(fmt, "csv") == 0) - opts_out->csv_mode = true; - else if (strcmp(fmt, "binary") == 0) - opts_out->binary = true; - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("COPY format \"%s\" not recognized", fmt), - parser_errposition(pstate, defel->location))); + ProcessCopyOptionFormat(pstate, opts_out, is_from, defel); } else if (strcmp(defel->defname, "freeze") == 0) { diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 1a59202f5ab..2b48c825a0a 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -244,7 +244,9 @@ static const CopyFromRoutine CopyFromRoutineBinary = { static const CopyFromRoutine * CopyFromGetRoutine(CopyFormatOptions opts) { - if (opts.csv_mode) + if (opts.routine) + return (const CopyFromRoutine *) opts.routine; + else if (opts.csv_mode) return &CopyFromRoutineCSV; else if (opts.binary) return &CopyFromRoutineBinary; diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index c7f69ba606d..a9e923467dc 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -435,7 +435,9 @@ static const CopyToRoutine CopyToRoutineBinary = { static const CopyToRoutine * CopyToGetRoutine(CopyFormatOptions opts) { - if (opts.csv_mode) + if (opts.routine) + return (const CopyToRoutine *) opts.routine; + else if (opts.csv_mode) return &CopyToRoutineCSV; else if (opts.binary) return &CopyToRoutineBinary; diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile index 66bbad8e6e0..173ee11811c 100644 --- a/src/backend/nodes/Makefile +++ b/src/backend/nodes/Makefile @@ -49,6 +49,7 @@ node_headers = \ access/sdir.h \ access/tableam.h \ access/tsmapi.h \ + commands/copyapi.h \ commands/event_trigger.h \ commands/trigger.h \ executor/tuptable.h \ diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl index 81df3bdf95f..428ab4f0d93 100644 --- a/src/backend/nodes/gen_node_support.pl +++ b/src/backend/nodes/gen_node_support.pl @@ -61,6 +61,7 @@ my @all_input_files = qw( access/sdir.h access/tableam.h access/tsmapi.h + commands/copyapi.h commands/event_trigger.h commands/trigger.h executor/tuptable.h @@ -85,6 +86,7 @@ my @nodetag_only_files = qw( access/sdir.h access/tableam.h access/tsmapi.h + commands/copyapi.h commands/event_trigger.h commands/trigger.h executor/tuptable.h diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index e189e9b79d2..25f24ab95d2 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -370,6 +370,7 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(fdw_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(index_am_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler); +PSEUDOTYPE_DUMMY_IO_FUNCS(copy_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(internal); PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement); PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray); diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 73d9cf85826..126254473e6 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7644,6 +7644,12 @@ { oid => '3312', descr => 'I/O', proname => 'tsm_handler_out', prorettype => 'cstring', proargtypes => 'tsm_handler', prosrc => 'tsm_handler_out' }, +{ oid => '8753', descr => 'I/O', + proname => 'copy_handler_in', proisstrict => 'f', prorettype => 'copy_handler', + proargtypes => 'cstring', prosrc => 'copy_handler_in' }, +{ oid => '8754', descr => 'I/O', + proname => 'copy_handler_out', prorettype => 'cstring', + proargtypes => 'copy_handler', prosrc => 'copy_handler_out' }, { oid => '267', descr => 'I/O', proname => 'table_am_handler_in', proisstrict => 'f', prorettype => 'table_am_handler', proargtypes => 'cstring', diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index ceff66ccde1..14c6c1ea486 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -633,6 +633,12 @@ typcategory => 'P', typinput => 'tsm_handler_in', typoutput => 'tsm_handler_out', typreceive => '-', typsend => '-', typalign => 'i' }, +{ oid => '8752', + descr => 'pseudo-type for the result of a copy to/from method functoin', + typname => 'copy_handler', typlen => '4', typbyval => 't', typtype => 'p', + typcategory => 'P', typinput => 'copy_handler_in', + typoutput => 'copy_handler_out', typreceive => '-', typsend => '-', + typalign => 'i' }, { oid => '269', descr => 'pseudo-type for the result of a table AM handler function', typname => 'table_am_handler', typlen => '4', typbyval => 't', typtype => 'p', diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h index ccfbdf0ee01..79bd4fb9151 100644 --- a/src/include/commands/copy.h +++ b/src/include/commands/copy.h @@ -84,6 +84,8 @@ typedef struct CopyFormatOptions CopyOnErrorChoice on_error; /* what to do when error happened */ CopyLogVerbosityChoice log_verbosity; /* verbosity of logged messages */ List *convert_select; /* list of column names (can be NIL) */ + Node *routine; /* CopyToRoutine or CopyFromRoutine (can be + * NULL) */ } CopyFormatOptions; /* These are private in commands/copy[from|to].c */ diff --git a/src/include/commands/copyapi.h b/src/include/commands/copyapi.h index d1289424c67..e049a45a4b1 100644 --- a/src/include/commands/copyapi.h +++ b/src/include/commands/copyapi.h @@ -27,6 +27,8 @@ typedef struct CopyToStateData *CopyToState; */ typedef struct CopyFromRoutine { + NodeTag type; + /* * Called when COPY FROM is started to set up the input functions * associated with the relation's attributes writing to. `finfo` can be @@ -69,6 +71,8 @@ typedef struct CopyFromRoutine */ typedef struct CopyToRoutine { + NodeTag type; + /* * Called when COPY TO is started to set up the output functions * associated with the relation's attributes reading from. `finfo` can be diff --git a/src/include/nodes/meson.build b/src/include/nodes/meson.build index b665e55b657..103df1a7873 100644 --- a/src/include/nodes/meson.build +++ b/src/include/nodes/meson.build @@ -11,6 +11,7 @@ node_support_input_i = [ 'access/sdir.h', 'access/tableam.h', 'access/tsmapi.h', + 'commands/copyapi.h', 'commands/event_trigger.h', 'commands/trigger.h', 'executor/tuptable.h', diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index 256799f520a..b7b46928a19 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -15,6 +15,7 @@ SUBDIRS = \ spgist_name_ops \ test_bloomfilter \ test_copy_callbacks \ + test_copy_format \ test_custom_rmgrs \ test_ddl_deparse \ test_dsa \ diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build index d8fe059d236..c42b4b2b31f 100644 --- a/src/test/modules/meson.build +++ b/src/test/modules/meson.build @@ -14,6 +14,7 @@ subdir('spgist_name_ops') subdir('ssl_passphrase_callback') subdir('test_bloomfilter') subdir('test_copy_callbacks') +subdir('test_copy_format') subdir('test_custom_rmgrs') subdir('test_ddl_deparse') subdir('test_dsa') diff --git a/src/test/modules/test_copy_format/.gitignore b/src/test/modules/test_copy_format/.gitignore new file mode 100644 index 00000000000..5dcb3ff9723 --- /dev/null +++ b/src/test/modules/test_copy_format/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/test_copy_format/Makefile b/src/test/modules/test_copy_format/Makefile new file mode 100644 index 00000000000..8497f91624d --- /dev/null +++ b/src/test/modules/test_copy_format/Makefile @@ -0,0 +1,23 @@ +# src/test/modules/test_copy_format/Makefile + +MODULE_big = test_copy_format +OBJS = \ + $(WIN32RES) \ + test_copy_format.o +PGFILEDESC = "test_copy_format - test custom COPY FORMAT" + +EXTENSION = test_copy_format +DATA = test_copy_format--1.0.sql + +REGRESS = test_copy_format + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/test_copy_format +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/test_copy_format/expected/test_copy_format.out b/src/test/modules/test_copy_format/expected/test_copy_format.out new file mode 100644 index 00000000000..4ed7c0b12db --- /dev/null +++ b/src/test/modules/test_copy_format/expected/test_copy_format.out @@ -0,0 +1,21 @@ +CREATE EXTENSION test_copy_format; +CREATE TABLE public.test (a smallint, b integer, c bigint); +INSERT INTO public.test VALUES (1, 2, 3), (12, 34, 56), (123, 456, 789); +COPY public.test FROM stdin WITH (format 'test_copy_format'); +NOTICE: test_copy_format: is_from=true +NOTICE: CopyFromInFunc: atttypid=21 +NOTICE: CopyFromInFunc: atttypid=23 +NOTICE: CopyFromInFunc: atttypid=20 +NOTICE: CopyFromStart: natts=3 +NOTICE: CopyFromOneRow +NOTICE: CopyFromEnd +COPY public.test TO stdout WITH (format 'test_copy_format'); +NOTICE: test_copy_format: is_from=false +NOTICE: CopyToOutFunc: atttypid=21 +NOTICE: CopyToOutFunc: atttypid=23 +NOTICE: CopyToOutFunc: atttypid=20 +NOTICE: CopyToStart: natts=3 +NOTICE: CopyToOneRow: tts_nvalid=3 +NOTICE: CopyToOneRow: tts_nvalid=3 +NOTICE: CopyToOneRow: tts_nvalid=3 +NOTICE: CopyToEnd diff --git a/src/test/modules/test_copy_format/meson.build b/src/test/modules/test_copy_format/meson.build new file mode 100644 index 00000000000..4cefe7b709a --- /dev/null +++ b/src/test/modules/test_copy_format/meson.build @@ -0,0 +1,33 @@ +# Copyright (c) 2024, PostgreSQL Global Development Group + +test_copy_format_sources = files( + 'test_copy_format.c', +) + +if host_system == 'windows' + test_copy_format_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'test_copy_format', + '--FILEDESC', 'test_copy_format - test custom COPY FORMAT',]) +endif + +test_copy_format = shared_module('test_copy_format', + test_copy_format_sources, + kwargs: pg_test_mod_args, +) +test_install_libs += test_copy_format + +test_install_data += files( + 'test_copy_format.control', + 'test_copy_format--1.0.sql', +) + +tests += { + 'name': 'test_copy_format', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'regress': { + 'sql': [ + 'test_copy_format', + ], + }, +} diff --git a/src/test/modules/test_copy_format/sql/test_copy_format.sql b/src/test/modules/test_copy_format/sql/test_copy_format.sql new file mode 100644 index 00000000000..e805f7cb011 --- /dev/null +++ b/src/test/modules/test_copy_format/sql/test_copy_format.sql @@ -0,0 +1,6 @@ +CREATE EXTENSION test_copy_format; +CREATE TABLE public.test (a smallint, b integer, c bigint); +INSERT INTO public.test VALUES (1, 2, 3), (12, 34, 56), (123, 456, 789); +COPY public.test FROM stdin WITH (format 'test_copy_format'); +\. +COPY public.test TO stdout WITH (format 'test_copy_format'); diff --git a/src/test/modules/test_copy_format/test_copy_format--1.0.sql b/src/test/modules/test_copy_format/test_copy_format--1.0.sql new file mode 100644 index 00000000000..d24ea03ce99 --- /dev/null +++ b/src/test/modules/test_copy_format/test_copy_format--1.0.sql @@ -0,0 +1,8 @@ +/* src/test/modules/test_copy_format/test_copy_format--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION test_copy_format" to load this file. \quit + +CREATE FUNCTION test_copy_format(internal) + RETURNS copy_handler + AS 'MODULE_PATHNAME' LANGUAGE C; diff --git a/src/test/modules/test_copy_format/test_copy_format.c b/src/test/modules/test_copy_format/test_copy_format.c new file mode 100644 index 00000000000..f6b105659ab --- /dev/null +++ b/src/test/modules/test_copy_format/test_copy_format.c @@ -0,0 +1,100 @@ +/*-------------------------------------------------------------------------- + * + * test_copy_format.c + * Code for testing custom COPY format. + * + * Portions Copyright (c) 2024, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/test_copy_format/test_copy_format.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "commands/copyapi.h" +#include "commands/defrem.h" + +PG_MODULE_MAGIC; + +static void +CopyFromInFunc(CopyFromState cstate, Oid atttypid, + FmgrInfo *finfo, Oid *typioparam) +{ + ereport(NOTICE, (errmsg("CopyFromInFunc: atttypid=%d", atttypid))); +} + +static void +CopyFromStart(CopyFromState cstate, TupleDesc tupDesc) +{ + ereport(NOTICE, (errmsg("CopyFromStart: natts=%d", tupDesc->natts))); +} + +static bool +CopyFromOneRow(CopyFromState cstate, ExprContext *econtext, Datum *values, bool *nulls) +{ + ereport(NOTICE, (errmsg("CopyFromOneRow"))); + return false; +} + +static void +CopyFromEnd(CopyFromState cstate) +{ + ereport(NOTICE, (errmsg("CopyFromEnd"))); +} + +static const CopyFromRoutine CopyFromRoutineTestCopyFormat = { + .type = T_CopyFromRoutine, + .CopyFromInFunc = CopyFromInFunc, + .CopyFromStart = CopyFromStart, + .CopyFromOneRow = CopyFromOneRow, + .CopyFromEnd = CopyFromEnd, +}; + +static void +CopyToOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo) +{ + ereport(NOTICE, (errmsg("CopyToOutFunc: atttypid=%d", atttypid))); +} + +static void +CopyToStart(CopyToState cstate, TupleDesc tupDesc) +{ + ereport(NOTICE, (errmsg("CopyToStart: natts=%d", tupDesc->natts))); +} + +static void +CopyToOneRow(CopyToState cstate, TupleTableSlot *slot) +{ + ereport(NOTICE, (errmsg("CopyToOneRow: tts_nvalid=%u", slot->tts_nvalid))); +} + +static void +CopyToEnd(CopyToState cstate) +{ + ereport(NOTICE, (errmsg("CopyToEnd"))); +} + +static const CopyToRoutine CopyToRoutineTestCopyFormat = { + .type = T_CopyToRoutine, + .CopyToOutFunc = CopyToOutFunc, + .CopyToStart = CopyToStart, + .CopyToOneRow = CopyToOneRow, + .CopyToEnd = CopyToEnd, +}; + +PG_FUNCTION_INFO_V1(test_copy_format); +Datum +test_copy_format(PG_FUNCTION_ARGS) +{ + bool is_from = PG_GETARG_BOOL(0); + + ereport(NOTICE, + (errmsg("test_copy_format: is_from=%s", is_from ? "true" : "false"))); + + if (is_from) + PG_RETURN_POINTER(&CopyFromRoutineTestCopyFormat); + else + PG_RETURN_POINTER(&CopyToRoutineTestCopyFormat); +} diff --git a/src/test/modules/test_copy_format/test_copy_format.control b/src/test/modules/test_copy_format/test_copy_format.control new file mode 100644 index 00000000000..f05a6362358 --- /dev/null +++ b/src/test/modules/test_copy_format/test_copy_format.control @@ -0,0 +1,4 @@ +comment = 'Test code for custom COPY format' +default_version = '1.0' +module_pathname = '$libdir/test_copy_format' +relocatable = true -- 2.45.2 From a6670ca32945b19f5c2a35b6f4094fa8286ed327 Mon Sep 17 00:00:00 2001 From: Sutou Kouhei <kou@clear-code.com> Date: Tue, 23 Jan 2024 14:54:10 +0900 Subject: [PATCH v19 4/5] Export CopyToStateData and CopyFromStateData It's for custom COPY TO/FROM format handlers implemented as extension. This just moves codes. This doesn't change codes except CopyDest/CopyFrom enum values. CopyDest/CopyFrom enum values such as COPY_FILE are conflicted each other. So COPY_DEST_ prefix instead of COPY_ prefix is used for CopyDest enum values and COPY_SOURCE_ prefix instead of COPY_PREFIX_ is used for CopyFrom enum values. For example, COPY_FILE in CopyDest is renamed to COPY_DEST_FILE and COPY_FILE in CopyFrom is renamed to COPY_SOURCE_FILE. Note that this isn't enough to implement custom COPY TO/FROM format handlers as extension. We'll do the followings in a subsequent commit: For custom COPY TO format handler: 1. Add an opaque space for custom COPY TO format handler 2. Export CopySendEndOfRow() to flush buffer For custom COPY FROM format handler: 1. Add an opaque space for custom COPY FROM format handler 2. Export CopyReadBinaryData() to read the next data --- src/backend/commands/copyfrom.c | 4 +- src/backend/commands/copyfromparse.c | 10 +- src/backend/commands/copyto.c | 77 +----- src/include/commands/copy.h | 78 +----- src/include/commands/copyapi.h | 306 ++++++++++++++++++++++- src/include/commands/copyfrom_internal.h | 165 ------------ 6 files changed, 320 insertions(+), 320 deletions(-) diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 2b48c825a0a..5902172b8df 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -1699,7 +1699,7 @@ BeginCopyFrom(ParseState *pstate, pg_encoding_to_char(GetDatabaseEncoding())))); } - cstate->copy_src = COPY_FILE; /* default */ + cstate->copy_src = COPY_SOURCE_FILE; /* default */ cstate->whereClause = whereClause; @@ -1827,7 +1827,7 @@ BeginCopyFrom(ParseState *pstate, if (data_source_cb) { progress_vals[1] = PROGRESS_COPY_TYPE_CALLBACK; - cstate->copy_src = COPY_CALLBACK; + cstate->copy_src = COPY_SOURCE_CALLBACK; cstate->data_source_cb = data_source_cb; } else if (pipe) diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c index 90824b47785..74844103228 100644 --- a/src/backend/commands/copyfromparse.c +++ b/src/backend/commands/copyfromparse.c @@ -180,7 +180,7 @@ ReceiveCopyBegin(CopyFromState cstate) for (i = 0; i < natts; i++) pq_sendint16(&buf, format); /* per-column formats */ pq_endmessage(&buf); - cstate->copy_src = COPY_FRONTEND; + cstate->copy_src = COPY_SOURCE_FRONTEND; cstate->fe_msgbuf = makeStringInfo(); /* We *must* flush here to ensure FE knows it can send. */ pq_flush(); @@ -248,7 +248,7 @@ CopyGetData(CopyFromState cstate, void *databuf, int minread, int maxread) switch (cstate->copy_src) { - case COPY_FILE: + case COPY_SOURCE_FILE: bytesread = fread(databuf, 1, maxread, cstate->copy_file); if (ferror(cstate->copy_file)) ereport(ERROR, @@ -257,7 +257,7 @@ CopyGetData(CopyFromState cstate, void *databuf, int minread, int maxread) if (bytesread == 0) cstate->raw_reached_eof = true; break; - case COPY_FRONTEND: + case COPY_SOURCE_FRONTEND: while (maxread > 0 && bytesread < minread && !cstate->raw_reached_eof) { int avail; @@ -340,7 +340,7 @@ CopyGetData(CopyFromState cstate, void *databuf, int minread, int maxread) bytesread += avail; } break; - case COPY_CALLBACK: + case COPY_SOURCE_CALLBACK: bytesread = cstate->data_source_cb(databuf, minread, maxread); break; } @@ -1188,7 +1188,7 @@ CopyReadLine(CopyFromState cstate, bool is_csv) * after \. up to the protocol end of copy data. (XXX maybe better * not to treat \. as special?) */ - if (cstate->copy_src == COPY_FRONTEND) + if (cstate->copy_src == COPY_SOURCE_FRONTEND) { int inbytes; diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index a9e923467dc..54aa6cdecaf 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -37,67 +37,6 @@ #include "utils/rel.h" #include "utils/snapmgr.h" -/* - * Represents the different dest cases we need to worry about at - * the bottom level - */ -typedef enum CopyDest -{ - COPY_FILE, /* to file (or a piped program) */ - COPY_FRONTEND, /* to frontend */ - COPY_CALLBACK, /* to callback function */ -} CopyDest; - -/* - * This struct contains all the state variables used throughout a COPY TO - * operation. - * - * Multi-byte encodings: all supported client-side encodings encode multi-byte - * characters by having the first byte's high bit set. Subsequent bytes of the - * character can have the high bit not set. When scanning data in such an - * encoding to look for a match to a single-byte (ie ASCII) character, we must - * use the full pg_encoding_mblen() machinery to skip over multibyte - * characters, else we might find a false match to a trailing byte. In - * supported server encodings, there is no possibility of a false match, and - * it's faster to make useless comparisons to trailing bytes than it is to - * invoke pg_encoding_mblen() to skip over them. encoding_embeds_ascii is true - * when we have to do it the hard way. - */ -typedef struct CopyToStateData -{ - /* format routine */ - const CopyToRoutine *routine; - - /* low-level state data */ - CopyDest copy_dest; /* type of copy source/destination */ - FILE *copy_file; /* used if copy_dest == COPY_FILE */ - StringInfo fe_msgbuf; /* used for all dests during COPY TO */ - - int file_encoding; /* file or remote side's character encoding */ - bool need_transcoding; /* file encoding diff from server? */ - bool encoding_embeds_ascii; /* ASCII can be non-first byte? */ - - /* parameters from the COPY command */ - Relation rel; /* relation to copy to */ - QueryDesc *queryDesc; /* executable query to copy from */ - List *attnumlist; /* integer list of attnums to copy */ - char *filename; /* filename, or NULL for STDOUT */ - bool is_program; /* is 'filename' a program to popen? */ - copy_data_dest_cb data_dest_cb; /* function for writing data */ - - CopyFormatOptions opts; - Node *whereClause; /* WHERE condition (or NULL) */ - - /* - * Working state - */ - MemoryContext copycontext; /* per-copy execution context */ - - FmgrInfo *out_functions; /* lookup info for output functions */ - MemoryContext rowcontext; /* per-row evaluation context */ - uint64 bytes_processed; /* number of bytes processed so far */ -} CopyToStateData; - /* DestReceiver for COPY (query) TO */ typedef struct { @@ -143,7 +82,7 @@ CopyToTextLikeSendEndOfRow(CopyToState cstate) { switch (cstate->copy_dest) { - case COPY_FILE: + case COPY_DEST_FILE: /* Default line termination depends on platform */ #ifndef WIN32 CopySendChar(cstate, '\n'); @@ -151,7 +90,7 @@ CopyToTextLikeSendEndOfRow(CopyToState cstate) CopySendString(cstate, "\r\n"); #endif break; - case COPY_FRONTEND: + case COPY_DEST_FRONTEND: /* The FE/BE protocol uses \n as newline for all platforms */ CopySendChar(cstate, '\n'); break; @@ -464,7 +403,7 @@ SendCopyBegin(CopyToState cstate) for (i = 0; i < natts; i++) pq_sendint16(&buf, format); /* per-column formats */ pq_endmessage(&buf); - cstate->copy_dest = COPY_FRONTEND; + cstate->copy_dest = COPY_DEST_FRONTEND; } static void @@ -511,7 +450,7 @@ CopySendEndOfRow(CopyToState cstate) switch (cstate->copy_dest) { - case COPY_FILE: + case COPY_DEST_FILE: if (fwrite(fe_msgbuf->data, fe_msgbuf->len, 1, cstate->copy_file) != 1 || ferror(cstate->copy_file)) @@ -545,11 +484,11 @@ CopySendEndOfRow(CopyToState cstate) errmsg("could not write to COPY file: %m"))); } break; - case COPY_FRONTEND: + case COPY_DEST_FRONTEND: /* Dump the accumulated row as one CopyData message */ (void) pq_putmessage(PqMsg_CopyData, fe_msgbuf->data, fe_msgbuf->len); break; - case COPY_CALLBACK: + case COPY_DEST_CALLBACK: cstate->data_dest_cb(fe_msgbuf->data, fe_msgbuf->len); break; } @@ -928,12 +867,12 @@ BeginCopyTo(ParseState *pstate, /* See Multibyte encoding comment above */ cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->file_encoding); - cstate->copy_dest = COPY_FILE; /* default */ + cstate->copy_dest = COPY_DEST_FILE; /* default */ if (data_dest_cb) { progress_vals[1] = PROGRESS_COPY_TYPE_CALLBACK; - cstate->copy_dest = COPY_CALLBACK; + cstate->copy_dest = COPY_DEST_CALLBACK; cstate->data_dest_cb = data_dest_cb; } else if (pipe) diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h index 79bd4fb9151..e2411848e9f 100644 --- a/src/include/commands/copy.h +++ b/src/include/commands/copy.h @@ -14,87 +14,11 @@ #ifndef COPY_H #define COPY_H -#include "nodes/execnodes.h" +#include "commands/copyapi.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "tcop/dest.h" -/* - * Represents whether a header line should be present, and whether it must - * match the actual names (which implies "true"). - */ -typedef enum CopyHeaderChoice -{ - COPY_HEADER_FALSE = 0, - COPY_HEADER_TRUE, - COPY_HEADER_MATCH, -} CopyHeaderChoice; - -/* - * Represents where to save input processing errors. More values to be added - * in the future. - */ -typedef enum CopyOnErrorChoice -{ - COPY_ON_ERROR_STOP = 0, /* immediately throw errors, default */ - COPY_ON_ERROR_IGNORE, /* ignore errors */ -} CopyOnErrorChoice; - -/* - * Represents verbosity of logged messages by COPY command. - */ -typedef enum CopyLogVerbosityChoice -{ - COPY_LOG_VERBOSITY_DEFAULT = 0, /* logs no additional messages, default */ - COPY_LOG_VERBOSITY_VERBOSE, /* logs additional messages */ -} CopyLogVerbosityChoice; - -/* - * A struct to hold COPY options, in a parsed form. All of these are related - * to formatting, except for 'freeze', which doesn't really belong here, but - * it's expedient to parse it along with all the other options. - */ -typedef struct CopyFormatOptions -{ - /* parameters from the COPY command */ - int file_encoding; /* file or remote side's character encoding, - * -1 if not specified */ - bool binary; /* binary format? */ - bool freeze; /* freeze rows on loading? */ - bool csv_mode; /* Comma Separated Value format? */ - CopyHeaderChoice header_line; /* header line? */ - char *null_print; /* NULL marker string (server encoding!) */ - int null_print_len; /* length of same */ - char *null_print_client; /* same converted to file encoding */ - char *default_print; /* DEFAULT marker string */ - int default_print_len; /* length of same */ - char *delim; /* column delimiter (must be 1 byte) */ - char *quote; /* CSV quote char (must be 1 byte) */ - char *escape; /* CSV escape char (must be 1 byte) */ - List *force_quote; /* list of column names */ - bool force_quote_all; /* FORCE_QUOTE *? */ - bool *force_quote_flags; /* per-column CSV FQ flags */ - List *force_notnull; /* list of column names */ - bool force_notnull_all; /* FORCE_NOT_NULL *? */ - bool *force_notnull_flags; /* per-column CSV FNN flags */ - List *force_null; /* list of column names */ - bool force_null_all; /* FORCE_NULL *? */ - bool *force_null_flags; /* per-column CSV FN flags */ - bool convert_selectively; /* do selective binary conversion? */ - CopyOnErrorChoice on_error; /* what to do when error happened */ - CopyLogVerbosityChoice log_verbosity; /* verbosity of logged messages */ - List *convert_select; /* list of column names (can be NIL) */ - Node *routine; /* CopyToRoutine or CopyFromRoutine (can be - * NULL) */ -} CopyFormatOptions; - -/* These are private in commands/copy[from|to].c */ -typedef struct CopyFromStateData *CopyFromState; -typedef struct CopyToStateData *CopyToState; - -typedef int (*copy_data_source_cb) (void *outbuf, int minread, int maxread); -typedef void (*copy_data_dest_cb) (void *data, int len); - extern void DoCopy(ParseState *pstate, const CopyStmt *stmt, int stmt_location, int stmt_len, uint64 *processed); diff --git a/src/include/commands/copyapi.h b/src/include/commands/copyapi.h index e049a45a4b1..e298b19860c 100644 --- a/src/include/commands/copyapi.h +++ b/src/include/commands/copyapi.h @@ -14,12 +14,83 @@ #ifndef COPYAPI_H #define COPYAPI_H +#include "commands/trigger.h" +#include "executor/execdesc.h" #include "executor/tuptable.h" #include "nodes/execnodes.h" -/* These are private in commands/copy[from|to].c */ +/* + * Represents whether a header line should be present, and whether it must + * match the actual names (which implies "true"). + */ +typedef enum CopyHeaderChoice +{ + COPY_HEADER_FALSE = 0, + COPY_HEADER_TRUE, + COPY_HEADER_MATCH, +} CopyHeaderChoice; + +/* + * Represents where to save input processing errors. More values to be added + * in the future. + */ +typedef enum CopyOnErrorChoice +{ + COPY_ON_ERROR_STOP = 0, /* immediately throw errors, default */ + COPY_ON_ERROR_IGNORE, /* ignore errors */ +} CopyOnErrorChoice; + +/* + * Represents verbosity of logged messages by COPY command. + */ +typedef enum CopyLogVerbosityChoice +{ + COPY_LOG_VERBOSITY_DEFAULT = 0, /* logs no additional messages, default */ + COPY_LOG_VERBOSITY_VERBOSE, /* logs additional messages */ +} CopyLogVerbosityChoice; + +/* + * A struct to hold COPY options, in a parsed form. All of these are related + * to formatting, except for 'freeze', which doesn't really belong here, but + * it's expedient to parse it along with all the other options. + */ +typedef struct CopyFormatOptions +{ + /* parameters from the COPY command */ + int file_encoding; /* file or remote side's character encoding, + * -1 if not specified */ + bool binary; /* binary format? */ + bool freeze; /* freeze rows on loading? */ + bool csv_mode; /* Comma Separated Value format? */ + CopyHeaderChoice header_line; /* header line? */ + char *null_print; /* NULL marker string (server encoding!) */ + int null_print_len; /* length of same */ + char *null_print_client; /* same converted to file encoding */ + char *default_print; /* DEFAULT marker string */ + int default_print_len; /* length of same */ + char *delim; /* column delimiter (must be 1 byte) */ + char *quote; /* CSV quote char (must be 1 byte) */ + char *escape; /* CSV escape char (must be 1 byte) */ + List *force_quote; /* list of column names */ + bool force_quote_all; /* FORCE_QUOTE *? */ + bool *force_quote_flags; /* per-column CSV FQ flags */ + List *force_notnull; /* list of column names */ + bool force_notnull_all; /* FORCE_NOT_NULL *? */ + bool *force_notnull_flags; /* per-column CSV FNN flags */ + List *force_null; /* list of column names */ + bool force_null_all; /* FORCE_NULL *? */ + bool *force_null_flags; /* per-column CSV FN flags */ + bool convert_selectively; /* do selective binary conversion? */ + CopyOnErrorChoice on_error; /* what to do when error happened */ + CopyLogVerbosityChoice log_verbosity; /* verbosity of logged messages */ + List *convert_select; /* list of column names (can be NIL) */ + Node *routine; /* CopyToRoutine or CopyFromRoutine (can be + * NULL) */ +} CopyFormatOptions; + +typedef int (*copy_data_source_cb) (void *outbuf, int minread, int maxread); + typedef struct CopyFromStateData *CopyFromState; -typedef struct CopyToStateData *CopyToState; /* * API structure for a COPY FROM format implementation. Note this must be @@ -65,6 +136,174 @@ typedef struct CopyFromRoutine void (*CopyFromEnd) (CopyFromState cstate); } CopyFromRoutine; +/* + * Represents the different source cases we need to worry about at + * the bottom level + */ +typedef enum CopySource +{ + COPY_SOURCE_FILE, /* from file (or a piped program) */ + COPY_SOURCE_FRONTEND, /* from frontend */ + COPY_SOURCE_CALLBACK, /* from callback function */ +} CopySource; + +/* + * Represents the end-of-line terminator type of the input + */ +typedef enum EolType +{ + EOL_UNKNOWN, + EOL_NL, + EOL_CR, + EOL_CRNL, +} EolType; + +/* + * Represents the insert method to be used during COPY FROM. + */ +typedef enum CopyInsertMethod +{ + CIM_SINGLE, /* use table_tuple_insert or ExecForeignInsert */ + CIM_MULTI, /* always use table_multi_insert or + * ExecForeignBatchInsert */ + CIM_MULTI_CONDITIONAL, /* use table_multi_insert or + * ExecForeignBatchInsert only if valid */ +} CopyInsertMethod; + +/* + * This struct contains all the state variables used throughout a COPY FROM + * operation. + */ +typedef struct CopyFromStateData +{ + /* format routine */ + const CopyFromRoutine *routine; + + /* low-level state data */ + CopySource copy_src; /* type of copy source */ + FILE *copy_file; /* used if copy_src == COPY_FILE */ + StringInfo fe_msgbuf; /* used if copy_src == COPY_FRONTEND */ + + EolType eol_type; /* EOL type of input */ + int file_encoding; /* file or remote side's character encoding */ + bool need_transcoding; /* file encoding diff from server? */ + Oid conversion_proc; /* encoding conversion function */ + + /* parameters from the COPY command */ + Relation rel; /* relation to copy from */ + List *attnumlist; /* integer list of attnums to copy */ + char *filename; /* filename, or NULL for STDIN */ + bool is_program; /* is 'filename' a program to popen? */ + copy_data_source_cb data_source_cb; /* function for reading data */ + + CopyFormatOptions opts; + bool *convert_select_flags; /* per-column CSV/TEXT CS flags */ + Node *whereClause; /* WHERE condition (or NULL) */ + + /* these are just for error messages, see CopyFromErrorCallback */ + const char *cur_relname; /* table name for error messages */ + uint64 cur_lineno; /* line number for error messages */ + const char *cur_attname; /* current att for error messages */ + const char *cur_attval; /* current att value for error messages */ + bool relname_only; /* don't output line number, att, etc. */ + + /* + * Working state + */ + MemoryContext copycontext; /* per-copy execution context */ + + AttrNumber num_defaults; /* count of att that are missing and have + * default value */ + FmgrInfo *in_functions; /* array of input functions for each attrs */ + Oid *typioparams; /* array of element types for in_functions */ + ErrorSaveContext *escontext; /* soft error trapper during in_functions + * execution */ + uint64 num_errors; /* total number of rows which contained soft + * errors */ + int *defmap; /* array of default att numbers related to + * missing att */ + ExprState **defexprs; /* array of default att expressions for all + * att */ + bool *defaults; /* if DEFAULT marker was found for + * corresponding att */ + bool volatile_defexprs; /* is any of defexprs volatile? */ + List *range_table; /* single element list of RangeTblEntry */ + List *rteperminfos; /* single element list of RTEPermissionInfo */ + ExprState *qualexpr; + + TransitionCaptureState *transition_capture; + + /* + * These variables are used to reduce overhead in COPY FROM. + * + * attribute_buf holds the separated, de-escaped text for each field of + * the current line. The CopyReadAttributes functions return arrays of + * pointers into this buffer. We avoid palloc/pfree overhead by re-using + * the buffer on each cycle. + * + * In binary COPY FROM, attribute_buf holds the binary data for the + * current field, but the usage is otherwise similar. + */ + StringInfoData attribute_buf; + + /* field raw data pointers found by COPY FROM */ + + int max_fields; + char **raw_fields; + + /* + * Similarly, line_buf holds the whole input line being processed. The + * input cycle is first to read the whole line into line_buf, and then + * extract the individual attribute fields into attribute_buf. line_buf + * is preserved unmodified so that we can display it in error messages if + * appropriate. (In binary mode, line_buf is not used.) + */ + StringInfoData line_buf; + bool line_buf_valid; /* contains the row being processed? */ + + /* + * input_buf holds input data, already converted to database encoding. + * + * In text mode, CopyReadLine parses this data sufficiently to locate line + * boundaries, then transfers the data to line_buf. We guarantee that + * there is a \0 at input_buf[input_buf_len] at all times. (In binary + * mode, input_buf is not used.) + * + * If encoding conversion is not required, input_buf is not a separate + * buffer but points directly to raw_buf. In that case, input_buf_len + * tracks the number of bytes that have been verified as valid in the + * database encoding, and raw_buf_len is the total number of bytes stored + * in the buffer. + */ +#define INPUT_BUF_SIZE 65536 /* we palloc INPUT_BUF_SIZE+1 bytes */ + char *input_buf; + int input_buf_index; /* next byte to process */ + int input_buf_len; /* total # of bytes stored */ + bool input_reached_eof; /* true if we reached EOF */ + bool input_reached_error; /* true if a conversion error happened */ + /* Shorthand for number of unconsumed bytes available in input_buf */ +#define INPUT_BUF_BYTES(cstate) ((cstate)->input_buf_len - (cstate)->input_buf_index) + + /* + * raw_buf holds raw input data read from the data source (file or client + * connection), not yet converted to the database encoding. Like with + * 'input_buf', we guarantee that there is a \0 at raw_buf[raw_buf_len]. + */ +#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */ + char *raw_buf; + int raw_buf_index; /* next byte to process */ + int raw_buf_len; /* total # of bytes stored */ + bool raw_reached_eof; /* true if we reached EOF */ + + /* Shorthand for number of unconsumed bytes available in raw_buf */ +#define RAW_BUF_BYTES(cstate) ((cstate)->raw_buf_len - (cstate)->raw_buf_index) + + uint64 bytes_processed; /* number of bytes processed so far */ +} CopyFromStateData; + + +typedef struct CopyToStateData *CopyToState; + /* * API structure for a COPY TO format implementation. Note this must be * allocated in a server-lifetime manner, typically as a static const struct. @@ -102,4 +341,67 @@ typedef struct CopyToRoutine void (*CopyToEnd) (CopyToState cstate); } CopyToRoutine; +/* + * Represents the different dest cases we need to worry about at + * the bottom level + */ +typedef enum CopyDest +{ + COPY_DEST_FILE, /* to file (or a piped program) */ + COPY_DEST_FRONTEND, /* to frontend */ + COPY_DEST_CALLBACK, /* to callback function */ +} CopyDest; + +typedef void (*copy_data_dest_cb) (void *data, int len); + +/* + * This struct contains all the state variables used throughout a COPY TO + * operation. + * + * Multi-byte encodings: all supported client-side encodings encode multi-byte + * characters by having the first byte's high bit set. Subsequent bytes of the + * character can have the high bit not set. When scanning data in such an + * encoding to look for a match to a single-byte (ie ASCII) character, we must + * use the full pg_encoding_mblen() machinery to skip over multibyte + * characters, else we might find a false match to a trailing byte. In + * supported server encodings, there is no possibility of a false match, and + * it's faster to make useless comparisons to trailing bytes than it is to + * invoke pg_encoding_mblen() to skip over them. encoding_embeds_ascii is true + * when we have to do it the hard way. + */ +typedef struct CopyToStateData +{ + /* format routine */ + const CopyToRoutine *routine; + + /* low-level state data */ + CopyDest copy_dest; /* type of copy source/destination */ + FILE *copy_file; /* used if copy_dest == COPY_FILE */ + StringInfo fe_msgbuf; /* used for all dests during COPY TO */ + + int file_encoding; /* file or remote side's character encoding */ + bool need_transcoding; /* file encoding diff from server? */ + bool encoding_embeds_ascii; /* ASCII can be non-first byte? */ + + /* parameters from the COPY command */ + Relation rel; /* relation to copy to */ + QueryDesc *queryDesc; /* executable query to copy from */ + List *attnumlist; /* integer list of attnums to copy */ + char *filename; /* filename, or NULL for STDOUT */ + bool is_program; /* is 'filename' a program to popen? */ + copy_data_dest_cb data_dest_cb; /* function for writing data */ + + CopyFormatOptions opts; + Node *whereClause; /* WHERE condition (or NULL) */ + + /* + * Working state + */ + MemoryContext copycontext; /* per-copy execution context */ + + FmgrInfo *out_functions; /* lookup info for output functions */ + MemoryContext rowcontext; /* per-row evaluation context */ + uint64 bytes_processed; /* number of bytes processed so far */ +} CopyToStateData; + #endif /* COPYAPI_H */ diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index c11b5ff3cc0..3863d26d5b7 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -19,171 +19,6 @@ #include "commands/trigger.h" #include "nodes/miscnodes.h" -/* - * Represents the different source cases we need to worry about at - * the bottom level - */ -typedef enum CopySource -{ - COPY_FILE, /* from file (or a piped program) */ - COPY_FRONTEND, /* from frontend */ - COPY_CALLBACK, /* from callback function */ -} CopySource; - -/* - * Represents the end-of-line terminator type of the input - */ -typedef enum EolType -{ - EOL_UNKNOWN, - EOL_NL, - EOL_CR, - EOL_CRNL, -} EolType; - -/* - * Represents the insert method to be used during COPY FROM. - */ -typedef enum CopyInsertMethod -{ - CIM_SINGLE, /* use table_tuple_insert or ExecForeignInsert */ - CIM_MULTI, /* always use table_multi_insert or - * ExecForeignBatchInsert */ - CIM_MULTI_CONDITIONAL, /* use table_multi_insert or - * ExecForeignBatchInsert only if valid */ -} CopyInsertMethod; - -/* - * This struct contains all the state variables used throughout a COPY FROM - * operation. - */ -typedef struct CopyFromStateData -{ - /* format routine */ - const CopyFromRoutine *routine; - - /* low-level state data */ - CopySource copy_src; /* type of copy source */ - FILE *copy_file; /* used if copy_src == COPY_FILE */ - StringInfo fe_msgbuf; /* used if copy_src == COPY_FRONTEND */ - - EolType eol_type; /* EOL type of input */ - int file_encoding; /* file or remote side's character encoding */ - bool need_transcoding; /* file encoding diff from server? */ - Oid conversion_proc; /* encoding conversion function */ - - /* parameters from the COPY command */ - Relation rel; /* relation to copy from */ - List *attnumlist; /* integer list of attnums to copy */ - char *filename; /* filename, or NULL for STDIN */ - bool is_program; /* is 'filename' a program to popen? */ - copy_data_source_cb data_source_cb; /* function for reading data */ - - CopyFormatOptions opts; - bool *convert_select_flags; /* per-column CSV/TEXT CS flags */ - Node *whereClause; /* WHERE condition (or NULL) */ - - /* these are just for error messages, see CopyFromErrorCallback */ - const char *cur_relname; /* table name for error messages */ - uint64 cur_lineno; /* line number for error messages */ - const char *cur_attname; /* current att for error messages */ - const char *cur_attval; /* current att value for error messages */ - bool relname_only; /* don't output line number, att, etc. */ - - /* - * Working state - */ - MemoryContext copycontext; /* per-copy execution context */ - - AttrNumber num_defaults; /* count of att that are missing and have - * default value */ - FmgrInfo *in_functions; /* array of input functions for each attrs */ - Oid *typioparams; /* array of element types for in_functions */ - ErrorSaveContext *escontext; /* soft error trapper during in_functions - * execution */ - uint64 num_errors; /* total number of rows which contained soft - * errors */ - int *defmap; /* array of default att numbers related to - * missing att */ - ExprState **defexprs; /* array of default att expressions for all - * att */ - bool *defaults; /* if DEFAULT marker was found for - * corresponding att */ - bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; /* single element list of RangeTblEntry */ - List *rteperminfos; /* single element list of RTEPermissionInfo */ - ExprState *qualexpr; - - TransitionCaptureState *transition_capture; - - /* - * These variables are used to reduce overhead in COPY FROM. - * - * attribute_buf holds the separated, de-escaped text for each field of - * the current line. The CopyReadAttributes functions return arrays of - * pointers into this buffer. We avoid palloc/pfree overhead by re-using - * the buffer on each cycle. - * - * In binary COPY FROM, attribute_buf holds the binary data for the - * current field, but the usage is otherwise similar. - */ - StringInfoData attribute_buf; - - /* field raw data pointers found by COPY FROM */ - - int max_fields; - char **raw_fields; - - /* - * Similarly, line_buf holds the whole input line being processed. The - * input cycle is first to read the whole line into line_buf, and then - * extract the individual attribute fields into attribute_buf. line_buf - * is preserved unmodified so that we can display it in error messages if - * appropriate. (In binary mode, line_buf is not used.) - */ - StringInfoData line_buf; - bool line_buf_valid; /* contains the row being processed? */ - - /* - * input_buf holds input data, already converted to database encoding. - * - * In text mode, CopyReadLine parses this data sufficiently to locate line - * boundaries, then transfers the data to line_buf. We guarantee that - * there is a \0 at input_buf[input_buf_len] at all times. (In binary - * mode, input_buf is not used.) - * - * If encoding conversion is not required, input_buf is not a separate - * buffer but points directly to raw_buf. In that case, input_buf_len - * tracks the number of bytes that have been verified as valid in the - * database encoding, and raw_buf_len is the total number of bytes stored - * in the buffer. - */ -#define INPUT_BUF_SIZE 65536 /* we palloc INPUT_BUF_SIZE+1 bytes */ - char *input_buf; - int input_buf_index; /* next byte to process */ - int input_buf_len; /* total # of bytes stored */ - bool input_reached_eof; /* true if we reached EOF */ - bool input_reached_error; /* true if a conversion error happened */ - /* Shorthand for number of unconsumed bytes available in input_buf */ -#define INPUT_BUF_BYTES(cstate) ((cstate)->input_buf_len - (cstate)->input_buf_index) - - /* - * raw_buf holds raw input data read from the data source (file or client - * connection), not yet converted to the database encoding. Like with - * 'input_buf', we guarantee that there is a \0 at raw_buf[raw_buf_len]. - */ -#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */ - char *raw_buf; - int raw_buf_index; /* next byte to process */ - int raw_buf_len; /* total # of bytes stored */ - bool raw_reached_eof; /* true if we reached EOF */ - - /* Shorthand for number of unconsumed bytes available in raw_buf */ -#define RAW_BUF_BYTES(cstate) ((cstate)->raw_buf_len - (cstate)->raw_buf_index) - - uint64 bytes_processed; /* number of bytes processed so far */ -} CopyFromStateData; - extern void ReceiveCopyBegin(CopyFromState cstate); extern void ReceiveCopyBinaryHeader(CopyFromState cstate); -- 2.45.2 From 0cb173c99599c0669891642e6f1adb414769ae8c Mon Sep 17 00:00:00 2001 From: Sutou Kouhei <kou@clear-code.com> Date: Tue, 23 Jan 2024 15:12:43 +0900 Subject: [PATCH v19 5/5] Add support for implementing custom COPY TO/FROM format as extension For custom COPY TO format implementation: * Add CopyToStateData::opaque that can be used to keep data for custom COPY TO format implementation * Export CopySendEndOfRow() to flush data in CopyToStateData::fe_msgbuf as CopyToStateFlush() For custom COPY FROM format implementation: * Add CopyFromStateData::opaque that can be used to keep data for custom COPY From format implementation * Export CopyReadBinaryData() to read the next data as CopyFromStateRead() --- src/backend/commands/copyfromparse.c | 14 ++++++++++++++ src/backend/commands/copyto.c | 14 ++++++++++++++ src/include/commands/copyapi.h | 10 ++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c index 74844103228..a115d7f9e26 100644 --- a/src/backend/commands/copyfromparse.c +++ b/src/backend/commands/copyfromparse.c @@ -739,6 +739,20 @@ CopyReadBinaryData(CopyFromState cstate, char *dest, int nbytes) return copied_bytes; } +/* + * CopyFromStateRead + * + * Export CopyReadBinaryData() for extensions. We want to keep + * CopyReadBinaryData() as a static function for + * optimization. CopyReadBinaryData() calls in this file may be optimized by + * a compiler. + */ +int +CopyFromStateRead(CopyFromState cstate, char *dest, int nbytes) +{ + return CopyReadBinaryData(cstate, dest, nbytes); +} + /* * Read raw fields in the next line for COPY FROM in text or csv mode. * Return false if no more lines. diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index 54aa6cdecaf..b8d0e996117 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -500,6 +500,20 @@ CopySendEndOfRow(CopyToState cstate) resetStringInfo(fe_msgbuf); } +/* + * CopyToStateFlush + * + * Export CopySendEndOfRow() for extensions. We want to keep + * CopySendEndOfRow() as a static function for + * optimization. CopySendEndOfRow() calls in this file may be optimized by a + * compiler. + */ +void +CopyToStateFlush(CopyToState cstate) +{ + CopySendEndOfRow(cstate); +} + /* * These functions do apply some data conversion */ diff --git a/src/include/commands/copyapi.h b/src/include/commands/copyapi.h index e298b19860c..5665408eaa0 100644 --- a/src/include/commands/copyapi.h +++ b/src/include/commands/copyapi.h @@ -299,8 +299,13 @@ typedef struct CopyFromStateData #define RAW_BUF_BYTES(cstate) ((cstate)->raw_buf_len - (cstate)->raw_buf_index) uint64 bytes_processed; /* number of bytes processed so far */ + + /* For custom format implementation */ + void *opaque; /* private space */ } CopyFromStateData; +extern int CopyFromStateRead(CopyFromState cstate, char *dest, int nbytes); + typedef struct CopyToStateData *CopyToState; @@ -402,6 +407,11 @@ typedef struct CopyToStateData FmgrInfo *out_functions; /* lookup info for output functions */ MemoryContext rowcontext; /* per-row evaluation context */ uint64 bytes_processed; /* number of bytes processed so far */ + + /* For custom format implementation */ + void *opaque; /* private space */ } CopyToStateData; +extern void CopyToStateFlush(CopyToState cstate); + #endif /* COPYAPI_H */ -- 2.45.2
pgsql-hackers by date: